pax_global_header 0000666 0000000 0000000 00000000064 14507372607 0014525 g ustar 00root root 0000000 0000000 52 comment=993afaa3ce6cb05d03443cfcf7eb87ae63d39772
guest-agent-20231004.02/ 0000775 0000000 0000000 00000000000 14507372607 0014425 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/.gitignore 0000664 0000000 0000000 00000000212 14507372607 0016410 0 ustar 00root root 0000000 0000000 # ignore all built binaries
**/gce_workload_cert_refresh
**/google_authorized_keys
**/google_guest_agent
**/google_metadata_script_runner
guest-agent-20231004.02/90-google-guest-agent.preset 0000664 0000000 0000000 00000000226 14507372607 0021574 0 ustar 00root root 0000000 0000000 enable google-guest-agent.service
enable google-shutdown-scripts.service
enable google-startup-scripts.service
enable gce-workload-cert-refresh.timer
guest-agent-20231004.02/CONTRIBUTING.md 0000664 0000000 0000000 00000005756 14507372607 0016673 0 ustar 00root root 0000000 0000000 # How to become a contributor and submit your own code
## Before you begin
### Sign our Contributor License Agreement
Contributions to this project must be accompanied by a
[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
You (or your employer) retain the copyright to your contribution; this simply
gives us permission to use and redistribute your contributions as part of the
project.
If you or your current employer have already signed the Google CLA (even if it
was for a different project), you probably don't need to do it again.
Visit to see your current agreements or to
sign a new one.
### Review our community guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
## 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-20231004.02/LICENSE 0000664 0000000 0000000 00000026134 14507372607 0015440 0 ustar 00root root 0000000 0000000 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-20231004.02/OWNERS 0000664 0000000 0000000 00000000440 14507372607 0015363 0 ustar 00root root 0000000 0000000 # This file enables automatic assignment of PR reviewers.
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- a-crate
- bkatyl
- chaitanyakulkarni28
- dorileo
- drewhli
- elicriffield
- jjerger
- karnvadaliya
- koln67
- quintonamore
- vorakl
- zmarano
guest-agent-20231004.02/README.md 0000664 0000000 0000000 00000024730 14507372607 0015712 0 ustar 00root root 0000000 0000000 ## 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)
* [Telemetry](#telemetry)
* [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.
#### Telemetry
The guest agent will record some basic system telemetry information at start and
then once every 24 hours.
* Guest agent version and architecture
* Operating system name and version
* Operating system kernel release and version
Telemetry can be disabled by setting the metadata key `disable-guest-telemetry`
to `true`.
## 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.
OSLogin | cert_authentication | `false` prevents guest-agent from setting up sshd's `TrustedUserCAKeys`, `AuthorizedPrincipalsCommand` and `AuthorizedPrincipalsCommandUser` configuration keys. Default value: `true`.
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-20231004.02/THIRD_PARTY_LICENSES/ 0000775 0000000 0000000 00000000000 14507372607 0017543 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/ 0000775 0000000 0000000 00000000000 14507372607 0022701 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/ 0000775 0000000 0000000 00000000000 14507372607 0023306 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0024323 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/compute/ 0000775 0000000 0000000 00000000000 14507372607 0024762 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/compute/metadata/ 0000775 0000000 0000000 00000000000 14507372607 0026542 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/compute/metadata/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0027557 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/logging/ 0000775 0000000 0000000 00000000000 14507372607 0024734 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/logging/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0025751 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/longrunning/ 0000775 0000000 0000000 00000000000 14507372607 0025646 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/longrunning/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0026663 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/storage/ 0000775 0000000 0000000 00000000000 14507372607 0024752 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/cloud.google.com/go/storage/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0025767 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/github.com/ 0000775 0000000 0000000 00000000000 14507372607 0021602 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/ 0000775 0000000 0000000 00000000000 14507372607 0025512 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-agent/ 0000775 0000000 0000000 00000000000 14507372607 0027735 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-agent/LICENSE 0000664 0000000 0000000 00000026115 14507372607 0030747 0 ustar 00root root 0000000 0000000 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.
google_guest_agent/ 0000775 0000000 0000000 00000000000 14507372607 0033517 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-agent LICENSE 0000664 0000000 0000000 00000026115 14507372607 0034531 0 ustar 00root root 0000000 0000000 guest-agent-20231004.02/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-20231004.02/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-logging-go/ 0000775 0000000 0000000 00000000000 14507372607 0030670 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/GoogleCloudPlatform/guest-logging-go/logger/0000775 0000000 0000000 00000000000 14507372607 0032147 5 ustar 00root root 0000000 0000000 LICENSE 0000664 0000000 0000000 00000026135 14507372607 0033104 0 ustar 00root root 0000000 0000000 guest-agent-20231004.02/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-20231004.02/THIRD_PARTY_LICENSES/github.com/go-ini/ 0000775 0000000 0000000 00000000000 14507372607 0022764 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/go-ini/ini/ 0000775 0000000 0000000 00000000000 14507372607 0023543 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/go-ini/ini/LICENSE 0000664 0000000 0000000 00000024015 14507372607 0024552 0 ustar 00root root 0000000 0000000 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:
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-20231004.02/THIRD_PARTY_LICENSES/github.com/golang/ 0000775 0000000 0000000 00000000000 14507372607 0023051 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/golang/groupcache/ 0000775 0000000 0000000 00000000000 14507372607 0025171 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/golang/groupcache/lru/ 0000775 0000000 0000000 00000000000 14507372607 0025773 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/golang/groupcache/lru/LICENSE 0000664 0000000 0000000 00000024041 14507372607 0027001 0 ustar 00root root 0000000 0000000 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:
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-20231004.02/THIRD_PARTY_LICENSES/github.com/golang/protobuf/ 0000775 0000000 0000000 00000000000 14507372607 0024711 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/golang/protobuf/LICENSE 0000664 0000000 0000000 00000002710 14507372607 0025716 0 ustar 00root root 0000000 0000000 Copyright 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-20231004.02/THIRD_PARTY_LICENSES/github.com/google/ 0000775 0000000 0000000 00000000000 14507372607 0023056 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-cmp/ 0000775 0000000 0000000 00000000000 14507372607 0024240 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-cmp/cmp/ 0000775 0000000 0000000 00000000000 14507372607 0025017 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-cmp/cmp/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0026032 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-sev-guest/ 0000775 0000000 0000000 00000000000 14507372607 0025403 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-sev-guest/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0026420 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-tpm-tools/ 0000775 0000000 0000000 00000000000 14507372607 0025417 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-tpm-tools/LICENSE 0000664 0000000 0000000 00000037017 14507372607 0026434 0 ustar 00root root 0000000 0000000
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.
--------------------------------------------------------------------
IBM simulator code (in tpm2-simulator/) uses the following license:
--------------------------------------------------------------------
(c) Copyright IBM Corporation 2016.
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 names of the IBM Corporation 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
HOLDER 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.
--------------------------------------------------------------------
A portion of the source code is derived from the TPM specification,
which has a TCG copyright. It is reproduced here for reference.
--------------------------------------------------------------------
Licenses and Notices
Copyright Licenses:
* Trusted Computing Group (TCG) grants to the user of the source code
in this specification (the "Source Code") a worldwide, irrevocable,
nonexclusive, royalty free, copyright license to reproduce, create
derivative works, distribute, display and perform the Source Code and
derivative works thereof, and to grant others the rights granted
herein.
* The TCG grants to the user of the other parts of the specification
(other than the Source Code) the rights to reproduce, distribute,
display, and perform the specification solely for the purpose of
developing products based on such documents.
Source Code Distribution Conditions:
* Redistributions of Source Code must retain the above copyright
licenses, this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright
licenses, this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
Disclaimers:
* THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF
LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH
RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES)
THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR
OTHERWISE. Contact TCG Administration
(admin@trustedcomputinggroup.org) for information on specification
licensing rights available through TCG membership agreements.
* THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED
WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR
NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY
OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE.
* Without limitation, TCG and its members and licensors disclaim all
liability, including liability for infringement of any proprietary
rights, relating to use of information in this specification and to
the implementation of this specification, and TCG disclaims all
liability for cost of procurement of substitute goods or services,
lost profits, loss of use, loss of data or any incidental,
consequential, direct, indirect, or special damages, whether under
contract, tort, warranty or otherwise, arising in any way out of use
or reliance upon this specification or any information herein.
Any marks and brands contained herein are the property of their
respective owners.
guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-tpm/ 0000775 0000000 0000000 00000000000 14507372607 0024261 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/go-tpm/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0025276 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/github.com/google/logger/ 0000775 0000000 0000000 00000000000 14507372607 0024335 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/logger/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0025352 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/github.com/google/s2a-go/ 0000775 0000000 0000000 00000000000 14507372607 0024146 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/s2a-go/LICENSE.md 0000664 0000000 0000000 00000026136 14507372607 0025562 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/github.com/google/tink/ 0000775 0000000 0000000 00000000000 14507372607 0024023 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/tink/go/ 0000775 0000000 0000000 00000000000 14507372607 0024430 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/tink/go/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0025445 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/github.com/google/uuid/ 0000775 0000000 0000000 00000000000 14507372607 0024024 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/google/uuid/LICENSE 0000664 0000000 0000000 00000002710 14507372607 0025031 0 ustar 00root root 0000000 0000000 Copyright (c) 2009,2014 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-20231004.02/THIRD_PARTY_LICENSES/github.com/googleapis/ 0000775 0000000 0000000 00000000000 14507372607 0023733 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/googleapis/enterprise-certificate-proxy/ 0000775 0000000 0000000 00000000000 14507372607 0031552 5 ustar 00root root 0000000 0000000 client/ 0000775 0000000 0000000 00000000000 14507372607 0032751 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/googleapis/enterprise-certificate-proxy LICENSE 0000664 0000000 0000000 00000026136 14507372607 0033766 0 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/googleapis/enterprise-certificate-proxy/client
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-20231004.02/THIRD_PARTY_LICENSES/github.com/googleapis/gax-go/ 0000775 0000000 0000000 00000000000 14507372607 0025115 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/googleapis/gax-go/v2/ 0000775 0000000 0000000 00000000000 14507372607 0025444 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/googleapis/gax-go/v2/LICENSE 0000664 0000000 0000000 00000002677 14507372607 0026465 0 ustar 00root root 0000000 0000000 Copyright 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-20231004.02/THIRD_PARTY_LICENSES/github.com/kardianos/ 0000775 0000000 0000000 00000000000 14507372607 0023555 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/kardianos/service/ 0000775 0000000 0000000 00000000000 14507372607 0025215 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/kardianos/service/LICENSE 0000664 0000000 0000000 00000001546 14507372607 0026230 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/github.com/pborman/ 0000775 0000000 0000000 00000000000 14507372607 0023240 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/pborman/uuid/ 0000775 0000000 0000000 00000000000 14507372607 0024206 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/pborman/uuid/LICENSE 0000664 0000000 0000000 00000002710 14507372607 0025213 0 ustar 00root root 0000000 0000000 Copyright (c) 2009,2014 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-20231004.02/THIRD_PARTY_LICENSES/github.com/pkg/ 0000775 0000000 0000000 00000000000 14507372607 0022363 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/pkg/errors/ 0000775 0000000 0000000 00000000000 14507372607 0023677 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/pkg/errors/LICENSE 0000664 0000000 0000000 00000002440 14507372607 0024704 0 ustar 00root root 0000000 0000000 Copyright (c) 2015, Dave Cheney
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.
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 HOLDER 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-20231004.02/THIRD_PARTY_LICENSES/github.com/robfig/ 0000775 0000000 0000000 00000000000 14507372607 0023052 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/robfig/cron/ 0000775 0000000 0000000 00000000000 14507372607 0024013 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/robfig/cron/v3/ 0000775 0000000 0000000 00000000000 14507372607 0024343 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/robfig/cron/v3/LICENSE 0000664 0000000 0000000 00000002104 14507372607 0025345 0 ustar 00root root 0000000 0000000 Copyright (C) 2012 Rob Figueiredo
All Rights Reserved.
MIT LICENSE
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/tarm/ 0000775 0000000 0000000 00000000000 14507372607 0022545 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/tarm/serial/ 0000775 0000000 0000000 00000000000 14507372607 0024024 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/github.com/tarm/serial/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0025037 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/go.opencensus.io/ 0000775 0000000 0000000 00000000000 14507372607 0022737 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/go.opencensus.io/LICENSE 0000664 0000000 0000000 00000026135 14507372607 0023753 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/golang.org/ 0000775 0000000 0000000 00000000000 14507372607 0021600 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/ 0000775 0000000 0000000 00000000000 14507372607 0022047 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/crypto/ 0000775 0000000 0000000 00000000000 14507372607 0023367 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/crypto/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0024402 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/net/ 0000775 0000000 0000000 00000000000 14507372607 0022635 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/net/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0023650 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/oauth2/ 0000775 0000000 0000000 00000000000 14507372607 0023251 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/oauth2/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0024264 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/sync/ 0000775 0000000 0000000 00000000000 14507372607 0023023 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/sync/semaphore/ 0000775 0000000 0000000 00000000000 14507372607 0025006 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/sync/semaphore/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0026021 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/sys/ 0000775 0000000 0000000 00000000000 14507372607 0022665 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/sys/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0023700 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/text/ 0000775 0000000 0000000 00000000000 14507372607 0023033 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/golang.org/x/text/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0024046 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/ 0000775 0000000 0000000 00000000000 14507372607 0023053 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/api/ 0000775 0000000 0000000 00000000000 14507372607 0023624 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/api/LICENSE 0000664 0000000 0000000 00000002703 14507372607 0024633 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/api/internal/ 0000775 0000000 0000000 00000000000 14507372607 0025440 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/api/internal/third_party/ 0000775 0000000 0000000 00000000000 14507372607 0027771 5 ustar 00root root 0000000 0000000 uritemplates/ 0000775 0000000 0000000 00000000000 14507372607 0032430 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/api/internal/third_party LICENSE 0000664 0000000 0000000 00000002706 14507372607 0033442 0 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/api/internal/third_party/uritemplates Copyright (c) 2013 Joshua Tacoma. 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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/appengine/ 0000775 0000000 0000000 00000000000 14507372607 0025021 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/appengine/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0026036 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/ 0000775 0000000 0000000 00000000000 14507372607 0024710 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0025725 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/googleapis/ 0000775 0000000 0000000 00000000000 14507372607 0027041 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/googleapis/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0030056 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/googleapis/api/ 0000775 0000000 0000000 00000000000 14507372607 0027612 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/googleapis/api/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0030627 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/googleapis/rpc/ 0000775 0000000 0000000 00000000000 14507372607 0027625 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/genproto/googleapis/rpc/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0030642 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/grpc/ 0000775 0000000 0000000 00000000000 14507372607 0024006 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/grpc/LICENSE 0000664 0000000 0000000 00000026136 14507372607 0025023 0 ustar 00root root 0000000 0000000
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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/grpc/NOTICE.txt 0000664 0000000 0000000 00000001052 14507372607 0025526 0 ustar 00root root 0000000 0000000 Copyright 2014 gRPC authors.
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-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/protobuf/ 0000775 0000000 0000000 00000000000 14507372607 0024713 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/google.golang.org/protobuf/LICENSE 0000664 0000000 0000000 00000002707 14507372607 0025726 0 ustar 00root root 0000000 0000000 Copyright (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-20231004.02/THIRD_PARTY_LICENSES/software.sslmate.com/ 0000775 0000000 0000000 00000000000 14507372607 0023621 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/software.sslmate.com/src/ 0000775 0000000 0000000 00000000000 14507372607 0024410 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/software.sslmate.com/src/go-pkcs12/ 0000775 0000000 0000000 00000000000 14507372607 0026116 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/THIRD_PARTY_LICENSES/software.sslmate.com/src/go-pkcs12/LICENSE 0000664 0000000 0000000 00000003010 14507372607 0027115 0 ustar 00root root 0000000 0000000 Copyright (c) 2015, 2018, 2019 Opsmate, Inc. All rights reserved.
Copyright (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-20231004.02/gce-workload-cert-refresh.service 0000664 0000000 0000000 00000000301 14507372607 0022746 0 ustar 00root root 0000000 0000000 [Unit]
Description=GCE Workload Certificate refresh
[Service]
Type=oneshot
ExecStart=/usr/bin/gce_workload_cert_refresh
# No [Install] section - this is controlled by gce-workload-cert.timer
guest-agent-20231004.02/gce-workload-cert-refresh.timer 0000664 0000000 0000000 00000000205 14507372607 0022431 0 ustar 00root root 0000000 0000000 [Unit]
Description=GCE Workload Certificate refresh timer
[Timer]
OnBootSec=5
OnUnitActiveSec=10m
[Install]
WantedBy=timers.target
guest-agent-20231004.02/gce_workload_cert_refresh/ 0000775 0000000 0000000 00000000000 14507372607 0021620 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/gce_workload_cert_refresh/main.go 0000664 0000000 0000000 00000022121 14507372607 0023071 0 ustar 00root root 0000000 0000000 // 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.
// gce_workload_cert_refresh downloads and rotates workload certificates for GCE VMs.
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"os"
"time"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
const (
contentDirPrefix = "/run/secrets/workload-spiffe-contents"
tempSymlinkPrefix = "/run/secrets/workload-spiffe-symlink"
symlink = "/run/secrets/workload-spiffe-credentials"
programName = "gce_workload_certs_refresh"
)
var (
// mdsClient is the client used to query Metadata server.
mdsClient *metadata.Client
)
func init() {
mdsClient = metadata.New()
}
func logFormat(e logger.LogEntry) string {
now := time.Now().Format("2006/01/02 15:04:05")
return fmt.Sprintf("%s: %s", now, e.Message)
}
func getMetadata(ctx context.Context, key string) ([]byte, error) {
// GCE Workload Certificate endpoints return 412 Precondition failed if the VM was
// never configured with valid config values at least once. Without valid config
// values GCE cannot provision the workload certificates.
resp, err := mdsClient.GetKey(ctx, key, nil)
if err != nil {
return []byte{}, fmt.Errorf("failed to GET %q from MDS with error: %w", key, err)
}
return []byte(resp), nil
}
/*
metadata key instance/gce-workload-certificates/workload-identities
{
"status": "OK",
"workloadCredentials": {
"PROJECT_ID.svc.id.goog": {
"metadata": {
"workload_creds_dir_path": "/var/run/secrets/workload-spiffe-credentials"
},
"certificatePem": "-----BEGIN CERTIFICATE-----datahere-----END CERTIFICATE-----",
"privateKeyPem": "-----BEGIN PRIVATE KEY-----datahere-----END PRIVATE KEY-----"
}
}
}
*/
// WorkloadIdentities represents Workload Identities in metadata.
type WorkloadIdentities struct {
Status string
WorkloadCredentials map[string]WorkloadCredential
}
// UnmarshalJSON is a custom JSON unmarshaller for WorkloadIdentities.
func (wi *WorkloadIdentities) UnmarshalJSON(b []byte) error {
tmp := map[string]json.RawMessage{}
err := json.Unmarshal(b, &tmp)
if err != nil {
return err
}
if err := json.Unmarshal(tmp["status"], &wi.Status); err != nil {
return err
}
wi.WorkloadCredentials = map[string]WorkloadCredential{}
wcs := map[string]json.RawMessage{}
if err := json.Unmarshal(tmp["workloadCredentials"], &wcs); err != nil {
return err
}
for domain, value := range wcs {
wc := WorkloadCredential{}
err := json.Unmarshal(value, &wc)
if err != nil {
return err
}
wi.WorkloadCredentials[domain] = wc
}
return nil
}
// WorkloadCredential represents Workload Credentials in metadata.
type WorkloadCredential struct {
Metadata Metadata
CertificatePem string
PrivateKeyPem string
}
/*
metadata key instance/gce-workload-certificates/root-certs
{
"status": "OK",
"rootCertificates": {
"PROJECT.svc.id.goog": {
"metadata": {
"workload_creds_dir_path": "/var/run/secrets/workload-spiffe-credentials"
},
"rootCertificatesPem": "-----BEGIN CERTIFICATE-----datahere-----END CERTIFICATE-----"
}
}
}
*/
// WorkloadTrustedRootCerts represents Workload Trusted Root Certs in metadata.
type WorkloadTrustedRootCerts struct {
Status string
RootCertificates map[string]RootCertificate
}
// UnmarshalJSON is a custom JSON unmarshaller for WorkloadTrustedRootCerts
func (wtrc *WorkloadTrustedRootCerts) UnmarshalJSON(b []byte) error {
tmp := map[string]json.RawMessage{}
err := json.Unmarshal(b, &tmp)
if err != nil {
return err
}
if err := json.Unmarshal(tmp["status"], &wtrc.Status); err != nil {
return err
}
wtrc.RootCertificates = map[string]RootCertificate{}
rcs := map[string]json.RawMessage{}
if err := json.Unmarshal(tmp["rootCertificates"], &rcs); err != nil {
return err
}
for domain, value := range rcs {
rc := RootCertificate{}
err := json.Unmarshal(value, &rc)
if err != nil {
return err
}
wtrc.RootCertificates[domain] = rc
}
return nil
}
// RootCertificate represents a Root Certificate in metadata
type RootCertificate struct {
Metadata Metadata
RootCertificatesPem string
}
// Metadata represents Metadata in metadata
type Metadata struct {
WorkloadCredsDirPath string
}
func main() {
ctx := context.Background()
opts := logger.LogOpts{
LoggerName: programName,
FormatFunction: logFormat,
// No need for syslog.
DisableLocalLogging: true,
}
opts.Writers = []io.Writer{os.Stderr}
logger.Init(ctx, opts)
defer logger.Infof("Done")
// TODO: prune old dirs
if err := refreshCreds(ctx); err != nil {
logger.Fatalf("Error refreshCreds: %v", err.Error())
}
}
func refreshCreds(ctx context.Context) error {
project, err := getMetadata(ctx, "project/project-id")
if err != nil {
return fmt.Errorf("error getting project ID: %v", err)
}
// Get status first so it can be written even when other endpoints are empty.
certConfigStatus, err := getMetadata(ctx, "instance/gce-workload-certificates/config-status")
if err != nil {
// Return success when certs are not configured to avoid unnecessary systemd failed units.
logger.Infof("Error getting config status, workload certificates may not be configured: %v", err)
return nil
}
domain := fmt.Sprintf("%s.svc.id.goog", project)
logger.Infof("Rotating workload credentials for trust domain %s", domain)
now := time.Now().Format(time.RFC3339)
contentDir := fmt.Sprintf("%s-%s", contentDirPrefix, now)
tempSymlink := fmt.Sprintf("%s-%s", tempSymlinkPrefix, now)
logger.Infof("Creating timestamp contents dir %s", contentDir)
if err := os.MkdirAll(contentDir, 0755); err != nil {
return fmt.Errorf("error creating contents dir: %v", err)
}
// Write config_status first even if remaining endpoints are empty.
if err := os.WriteFile(fmt.Sprintf("%s/config_status", contentDir), certConfigStatus, 0644); err != nil {
return fmt.Errorf("error writing config_status: %v", err)
}
// Handles the edge case where the config values provided for the first time may be invalid. This ensures
// that the symlink directory alwasys exists and contains the config_status to surface config errors to the VM.
if _, err := os.Stat(symlink); os.IsNotExist(err) {
logger.Infof("Creating new symlink %s", symlink)
if err := os.Symlink(contentDir, symlink); err != nil {
return fmt.Errorf("error creating symlink: %v", err)
}
}
// Now get the rest of the content.
wisMd, err := getMetadata(ctx, "instance/gce-workload-certificates/workload-identities")
if err != nil {
return fmt.Errorf("error getting workload-identities: %v", err)
}
wtrcsMd, err := getMetadata(ctx, "instance/gce-workload-certificates/root-certs")
if err != nil {
return fmt.Errorf("error getting workload-trusted-root-certs: %v", err)
}
wis := WorkloadIdentities{}
if err := json.Unmarshal(wisMd, &wis); err != nil {
return fmt.Errorf("error unmarshaling workload identities response: %v", err)
}
wtrcs := WorkloadTrustedRootCerts{}
if err := json.Unmarshal(wtrcsMd, &wtrcs); err != nil {
return fmt.Errorf("error unmarshaling workload trusted root certs: %v", err)
}
if err := os.WriteFile(fmt.Sprintf("%s/certificates.pem", contentDir), []byte(wis.WorkloadCredentials[domain].CertificatePem), 0644); err != nil {
return fmt.Errorf("error writing certificates.pem: %v", err)
}
if err := os.WriteFile(fmt.Sprintf("%s/private_key.pem", contentDir), []byte(wis.WorkloadCredentials[domain].PrivateKeyPem), 0644); err != nil {
return fmt.Errorf("error writing private_key.pem: %v", err)
}
if err := os.WriteFile(fmt.Sprintf("%s/ca_certificates.pem", contentDir), []byte(wtrcs.RootCertificates[domain].RootCertificatesPem), 0644); err != nil {
return fmt.Errorf("error writing ca_certificates.pem: %v", err)
}
if err := os.Symlink(contentDir, tempSymlink); err != nil {
return fmt.Errorf("error creating temporary link: %v", err)
}
oldTarget, err := os.Readlink(symlink)
if err != nil {
logger.Infof("Error reading existing symlink: %v\n", err)
oldTarget = ""
}
// Only rotate on success of all steps above.
logger.Infof("Rotating symlink %s", symlink)
if err := os.Rename(tempSymlink, symlink); err != nil {
return fmt.Errorf("error rotating target link: %v", err)
}
// Clean up previous contents dir.
newTarget, err := os.Readlink(symlink)
if err != nil {
return fmt.Errorf("error reading new symlink: %v, unable to remove old symlink target", err)
}
if oldTarget != newTarget {
logger.Infof("Removing old content dir %s", oldTarget)
if err := os.RemoveAll(oldTarget); err != nil {
return fmt.Errorf("failed to remove old symlink target: %v", err)
}
}
return nil
}
guest-agent-20231004.02/go.mod 0000664 0000000 0000000 00000004170 14507372607 0015535 0 ustar 00root root 0000000 0000000 module github.com/GoogleCloudPlatform/guest-agent
go 1.20
replace github.com/GoogleCloudPlatform/guest-agent/metadata => ../metadata
require (
cloud.google.com/go/storage v1.31.0
github.com/GoogleCloudPlatform/guest-logging-go v0.0.0-20230710215706-450679fd88a9
github.com/go-ini/ini v1.66.6
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golang/protobuf v1.5.3
github.com/google/go-tpm v0.9.0
github.com/google/go-tpm-tools v0.4.0
github.com/google/tink/go v1.7.0
github.com/kardianos/service v1.2.1
github.com/robfig/cron/v3 v3.0.1
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
golang.org/x/sys v0.11.0
google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.31.0
software.sslmate.com/src/go-pkcs12 v0.2.1
)
require (
cloud.google.com/go v0.110.6 // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.1 // indirect
cloud.google.com/go/logging v1.7.0 // indirect
cloud.google.com/go/longrunning v0.5.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-sev-guest v0.7.0 // indirect
github.com/google/logger v1.1.1 // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.134.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e // indirect
)
guest-agent-20231004.02/go.sum 0000664 0000000 0000000 00000056365 14507372607 0015577 0 ustar 00root root 0000000 0000000 cloud.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.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q=
cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I=
cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc=
cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
cloud.google.com/go/storage v1.31.0/go.mod h1:81ams1PrhW16L4kF7qg+4mTq7SRs5HsbDTM0bWvrwJ0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/GoogleCloudPlatform/guest-logging-go v0.0.0-20230710215706-450679fd88a9 h1:b3geIwOPAShYtR4F0XFt+2NJXTHVTfbxUFmrpiZXHdQ=
github.com/GoogleCloudPlatform/guest-logging-go v0.0.0-20230710215706-450679fd88a9/go.mod h1:6ZqSUIZRAPR5dNMWJ+FwIarFFQ9t5qalaKQs20o6h+I=
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/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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-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-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-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
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-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-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/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.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.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/certificate-transparency-go v1.1.2 h1:4hE0GEId6NAW28dFpC+LrRGwQX5dtmXQGDbg8+/MZOM=
github.com/google/go-attestation v0.4.4-0.20230613144338-a9b6eb1eb888 h1:HURgKPRPJSozDuMHpjdV+iyFVLhB6bi1JanhGgSzI1k=
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.5.0/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-sev-guest v0.7.0 h1:DBCABhTo7WicP27ZH/hwcCdjcmxFkxxMOQXm5hFcfp4=
github.com/google/go-sev-guest v0.7.0/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q=
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/google/go-tpm-tools v0.4.0 h1:bYRZAUvQEmn11WTKCkTLRCCv4aTlOBgBBeqCK0ABT2A=
github.com/google/go-tpm-tools v0.4.0/go.mod h1:G7PFUk8KKQzdYYGv/cpV9LB9sPT7czAAomnceugzNKQ=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ=
github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w=
github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
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/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
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-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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/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.3.0/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.134.0 h1:ktL4Goua+UBgoP1eL1/60LwZJqa1sIzkLmvoR3hR6Gw=
google.golang.org/api v0.134.0/go.mod h1:sjRL3UnjTx5UqNQS9EWr9N8p7xbHpy1k0XGRLCf3Spk=
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.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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg=
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw=
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e h1:S83+ibolgyZ0bqz7KEsUOPErxcv4VzlszxY+31OfB/E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
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.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
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.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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8=
software.sslmate.com/src/go-pkcs12 v0.2.1/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
guest-agent-20231004.02/google-guest-agent.conf 0000664 0000000 0000000 00000000164 14507372607 0020772 0 ustar 00root root 0000000 0000000 description "GCE Guest Agent"
start on stopped rc RUNLEVEL=[2345]
oom -16
respawn
exec /usr/bin/google_guest_agent
guest-agent-20231004.02/google-guest-agent.service 0000664 0000000 0000000 00000001570 14507372607 0021507 0 ustar 00root root 0000000 0000000 [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-20231004.02/google-shutdown-scripts.conf 0000664 0000000 0000000 00000000206 14507372607 0022104 0 ustar 00root root 0000000 0000000 # Runs a shutdown script from metadata.
start on starting rc RUNLEVEL=[06]
task
exec /usr/bin/google_metadata_script_runner shutdown
guest-agent-20231004.02/google-shutdown-scripts.service 0000664 0000000 0000000 00000000635 14507372607 0022625 0 ustar 00root root 0000000 0000000 [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-20231004.02/google-startup-scripts.conf 0000664 0000000 0000000 00000000214 14507372607 0021732 0 ustar 00root root 0000000 0000000 # Runs a startup script from metadata.
start on started google-guest-agent and startup
exec /usr/bin/google_metadata_script_runner startup
guest-agent-20231004.02/google-startup-scripts.service 0000664 0000000 0000000 00000000611 14507372607 0022446 0 ustar 00root root 0000000 0000000 [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-20231004.02/google_authorized_keys/ 0000775 0000000 0000000 00000000000 14507372607 0021172 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_authorized_keys/main.go 0000664 0000000 0000000 00000012540 14507372607 0022447 0 ustar 00root root 0000000 0000000 // 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
// https://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"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
"github.com/GoogleCloudPlatform/guest-agent/utils"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
var (
programName = "GoogleAuthorizedKeysCommand"
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 := io.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 {
err = utils.ValidateUserKey(user, keyVal)
}
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.Print(strings.Join(userKeyList, "\n"))
}
guest-agent-20231004.02/google_authorized_keys/main_test.go 0000664 0000000 0000000 00000015545 14507372607 0023516 0 ustar 00root root 0000000 0000000 // 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
// https://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-20231004.02/google_guest_agent/ 0000775 0000000 0000000 00000000000 14507372607 0020266 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/accounts_unix.go 0000664 0000000 0000000 00000003176 14507372607 0023506 0 ustar 00root root 0000000 0000000 // Copyright 2019 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
// https://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.
//go:build !windows
package main
import (
"context"
"fmt"
"os"
"os/user"
"syscall"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
)
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(ctx context.Context, username, uid string) error {
config := cfg.Get()
useradd := config.Accounts.UserAddCmd
if uid != "" {
useradd = fmt.Sprintf("%s -u %s", useradd, uid)
}
cmd, args := createUserGroupCmd(useradd, username, "")
return run.Quiet(ctx, cmd, args...)
}
func addUserToGroup(ctx context.Context, user, group string) error {
config := cfg.Get()
gpasswdadd := config.Accounts.GPasswdAddCmd
cmd, args := createUserGroupCmd(gpasswdadd, user, group)
return run.Quiet(ctx, cmd, args...)
}
func userExists(name string) (bool, error) {
if _, err := user.Lookup(name); err != nil {
return false, err
}
return true, nil
}
guest-agent-20231004.02/google_guest_agent/accounts_windows.go 0000664 0000000 0000000 00000012157 14507372607 0024214 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"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(ctx context.Context, 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(ctx context.Context, 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-20231004.02/google_guest_agent/addresses.go 0000664 0000000 0000000 00000045712 14507372607 0022603 0 ustar 00root root 0000000 0000000 // 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 (
"context"
"errors"
"fmt"
"net"
"os"
"reflect"
"runtime"
"strings"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"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
)
type addressMgr struct{}
func (a *addressMgr) parseWSFCAddresses(config *cfg.Sections) string {
if config.WSFC != nil && config.WSFC.Addresses != "" {
return config.WSFC.Addresses
}
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(config *cfg.Sections) bool {
if config.WSFC != nil {
return config.WSFC.Enable
}
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(ctx context.Context, config *cfg.Sections, ifname string) ([]string, error) {
if runtime.GOOS == "windows" {
return nil, errors.New("getLocalRoutes unimplemented on Windows")
}
protoID := config.IPForwarding.EthernetProtoID
args := fmt.Sprintf("route list table local type local scope host dev %s proto %s", ifname, protoID)
out := run.WithOutput(ctx, "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 = run.WithOutput(ctx, "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(ctx context.Context, config *cfg.Sections, 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.IPForwarding.EthernetProtoID
args := fmt.Sprintf("route add to local %s scope host dev %s proto %s", ip, ifname, protoID)
return run.Quiet(ctx, "ip", strings.Split(args, " ")...)
}
// TODO: removeLocalRoute should be changed to removeIPForwardEntry and match getIPForwardEntries.
func removeLocalRoute(ctx context.Context, config *cfg.Sections, 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.IPForwarding.EthernetProtoID
args := fmt.Sprintf("route delete to local %s scope host dev %s proto %s", ip, ifname, protoID)
return run.Quiet(ctx, "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(config *cfg.Sections) {
wsfcAddresses := a.parseWSFCAddresses(config)
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(config)
if wsfcEnable {
for idx := range newMetadata.Instance.NetworkInterfaces {
newMetadata.Instance.NetworkInterfaces[idx].ForwardedIps = nil
newMetadata.Instance.NetworkInterfaces[idx].TargetInstanceIps = nil
}
}
}
}
func (a *addressMgr) Diff(ctx context.Context) (bool, error) {
config := cfg.Get()
wsfcAddresses := a.parseWSFCAddresses(config)
wsfcEnable := a.parseWSFCEnable(config)
diff := !reflect.DeepEqual(newMetadata.Instance.NetworkInterfaces, oldMetadata.Instance.NetworkInterfaces) ||
wsfcEnable != oldWSFCEnable || wsfcAddresses != oldWSFCAddresses
oldWSFCAddresses = wsfcAddresses
oldWSFCEnable = wsfcEnable
return diff, nil
}
func (a *addressMgr) Timeout(ctx context.Context) (bool, error) {
return false, nil
}
func (a *addressMgr) Disabled(ctx context.Context) (bool, error) {
config := cfg.Get()
// Local configuration takes precedence over metadata's configuration.
if config.AddressManager != nil {
return config.AddressManager.Disable, nil
}
if newMetadata.Instance.Attributes.DisableAddressManager != nil {
return *newMetadata.Instance.Attributes.DisableAddressManager, nil
}
if newMetadata.Project.Attributes.DisableAddressManager != nil {
return *newMetadata.Project.Attributes.DisableAddressManager, nil
}
// 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.Daemons.NetworkDaemon, nil
}
func (a *addressMgr) Set(ctx context.Context) error {
config := cfg.Get()
if runtime.GOOS == "windows" {
a.applyWSFCFilter(config)
}
var err error
interfaces, err = net.Interfaces()
if err != nil {
return fmt.Errorf("error populating interfaces: %v", err)
}
if config.NetworkInterfaces.Setup {
if runtime.GOOS != "windows" {
logger.Debugf("Configure IPv6")
if err := configureIPv6(ctx); 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(ctx, config); err != nil {
return err
}
interfacesEnabled = true
}
}
if !config.NetworkInterfaces.IPForwarding {
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.IPForwarding.TargetInstanceIPs {
wantIPs = append(wantIPs, ni.TargetInstanceIps...)
}
// IP Aliases are not supported on windows.
if runtime.GOOS != "windows" && config.IPForwarding.IPAliases {
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(ctx, config, 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(ctx, config, 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(ctx, config, 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(ctx context.Context) error {
var newNi, oldNi metadata.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 := []string{"-6", "-o", "a", "s", "dev", iface.Name, "scope", "link", "tentative"}
for i := 0; i < 5; i++ {
res := run.WithOutput(ctx, "ip", tentative...)
if res.ExitCode == 0 && res.StdOut == "" {
break
}
time.Sleep(1 * time.Second)
}
if err := run.Quiet(ctx, "dhclient", "-r", "-6", "-1", "-v", iface.Name); err != nil {
return err
}
case oldNi.DHCPv6Refresh == "" && newNi.DHCPv6Refresh != "":
// enable
tentative := []string{"-6", "-o", "a", "s", "dev", iface.Name, "scope", "link", "tentative"}
for i := 0; i < 5; i++ {
res := run.WithOutput(ctx, "ip", 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 := run.Quiet(ctx, "sysctl", val); err != nil {
return err
}
if err := run.Quiet(ctx, "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(ctx context.Context, config *cfg.Sections) 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 osInfo.OS == "sles":
return enableSLESInterfaces(ctx, googleInterfaces)
case (osInfo.OS == "rhel" || osInfo.OS == "centos") && osInfo.Version.Major >= 7:
for _, iface := range googleInterfaces {
err := disableNM(iface)
if err != nil {
return err
}
}
fallthrough
default:
dhcpCommand := config.NetworkInterfaces.DHCPCommand
if dhcpCommand != "" {
tokens := strings.Split(dhcpCommand, " ")
return run.Quiet(ctx, tokens[0], tokens[1:]...)
}
// Try IPv4 first as it's higher priority.
if err := run.Quiet(ctx, "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 := run.Quiet(ctx, "sysctl", val); err != nil {
return err
}
}
var dhclientArgs6 []string
dhclientArgs6 = append([]string{"-6"}, googleIpv6Interfaces...)
return run.Quiet(ctx, "dhclient", dhclientArgs6...)
}
}
// enableSLESInterfaces writes one ifcfg file for each interface, then
// runs `wicked ifup eth1 eth2 ... ethN`
func enableSLESInterfaces(ctx context.Context, 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 run.Quiet(ctx, "/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-20231004.02/google_guest_agent/addresses_integ_test.go 0000664 0000000 0000000 00000005411 14507372607 0025020 0 ustar 00root root 0000000 0000000 // 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
// https://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.
//go:build integration
// +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-20231004.02/google_guest_agent/addresses_test.go 0000664 0000000 0000000 00000023460 14507372607 0023636 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"reflect"
"testing"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
)
func reloadConfig(t *testing.T, extraDefaults []byte) {
t.Helper()
if err := cfg.Load(extraDefaults); err != nil {
t.Fatalf("Error parsing config: %+v", err)
}
}
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.Descriptor
want bool
}{
{"not explicitly disabled", []byte(""), &metadata.Descriptor{}, false},
{"enabled in cfg only", []byte("[addressManager]\ndisable=false"), &metadata.Descriptor{}, false},
{"disabled in cfg only", []byte("[addressManager]\ndisable=true"), &metadata.Descriptor{}, true},
{"disabled in cfg, enabled in instance metadata", []byte("[addressManager]\ndisable=true"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAddressManager: mkptr(false)}}}, true},
{"enabled in cfg, disabled in instance metadata", []byte("[addressManager]\ndisable=false"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAddressManager: mkptr(true)}}}, false},
{"enabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAddressManager: mkptr(false)}}}, false},
{"enabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{DisableAddressManager: mkptr(false)}}}, false},
{"disabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAddressManager: mkptr(true)}}}, true},
{"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAddressManager: mkptr(false)}}, Project: metadata.Project{Attributes: metadata.Attributes{DisableAddressManager: mkptr(true)}}}, false},
{"disabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{DisableAddressManager: mkptr(true)}}}, true},
}
ctx := context.Background()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reloadConfig(t, tt.data)
newMetadata = tt.md
got, err := (&addressMgr{}).Disabled(ctx)
if err != nil {
t.Errorf("Failed to run addressMgr's Disabled() call, got error: %+v", err)
}
if got != tt.want {
t.Errorf("addressMgr.Disabled() got: %t, want: %t", got, tt.want)
}
})
}
}
func TestAddressDiff(t *testing.T) {
var tests = []struct {
name string
data []byte
md *metadata.Descriptor
want bool
}{
{"not set", []byte(""), &metadata.Descriptor{}, false},
{"enabled in cfg only", []byte("[wsfc]\nenable=true"), &metadata.Descriptor{}, true},
{"disabled in cfg only", []byte("[wsfc]\nenable=false"), &metadata.Descriptor{}, false},
{"disabled in cfg, enabled in instance metadata", []byte("[wsfc]\nenable=false"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableWSFC: mkptr(true)}}}, false},
{"enabled in cfg, disabled in instance metadata", []byte("[wsfc]\nenable=true"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableWSFC: mkptr(false)}}}, true},
{"enabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableWSFC: mkptr(true)}}}, true},
{"enabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{EnableWSFC: mkptr(true)}}}, true},
{"disabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableWSFC: mkptr(false)}}}, false},
{"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableWSFC: mkptr(true)}}, Project: metadata.Project{Attributes: metadata.Attributes{EnableWSFC: mkptr(false)}}}, true},
{"disabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{EnableWSFC: mkptr(false)}}}, false},
}
ctx := context.Background()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reloadConfig(t, tt.data)
oldWSFCEnable = false
oldMetadata = &metadata.Descriptor{}
newMetadata = tt.md
got, err := (&addressMgr{}).Diff(ctx)
if err != nil {
t.Errorf("Failed to run addressMgr's Diff() call, got error: %+v", err)
}
if got != tt.want {
t.Errorf("addresses.diff() got: %t, want: %t", 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"}},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
var md metadata.Descriptor
reloadConfig(t, nil)
if err := json.Unmarshal(tt.metaDataJSON, &md); err != nil {
t.Error("failed to unmarshal test JSON:", tt, err)
}
newMetadata = &md
testAddress := addressMgr{}
testAddress.applyWSFCFilter(cfg.Get())
forwardedIps := []string{}
for _, ni := range newMetadata.Instance.NetworkInterfaces {
forwardedIps = append(forwardedIps, ni.ForwardedIps...)
}
if !reflect.DeepEqual(forwardedIps, tt.expectedIps) {
t.Errorf("wsfc filter failed: expect - %q, actual - %q", tt.expectedIps, forwardedIps)
}
})
}
}
func TestWsfcFlagTriggerAddressDiff(t *testing.T) {
var tests = []struct {
newMetadata, oldMetadata *metadata.Descriptor
}{
// trigger diff on wsfc-addrs
{&metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata.Descriptor{}},
{&metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata.Descriptor{}},
{&metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{WSFCAddresses: "192.168.0.2"}}}},
{&metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{WSFCAddresses: "192.168.0.1"}}}, &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{WSFCAddresses: "192.168.0.2"}}}},
}
ctx := context.Background()
for i, tt := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
reloadConfig(t, nil)
oldWSFCAddresses = tt.oldMetadata.Instance.Attributes.WSFCAddresses
newMetadata = tt.newMetadata
oldMetadata = tt.oldMetadata
testAddress := addressMgr{}
diff, err := testAddress.Diff(ctx)
if err != nil {
t.Errorf("Failed to run addressMgr's Diff() call, got error: %+v", err)
}
if !diff {
t.Errorf("old: %v new: %v doesn't trigger diff.", tt.oldMetadata, tt.newMetadata)
}
})
}
}
guest-agent-20231004.02/google_guest_agent/addresses_unix.go 0000664 0000000 0000000 00000002544 14507372607 0023642 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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.
//go:build !windows
// +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-20231004.02/google_guest_agent/addresses_windows.go 0000664 0000000 0000000 00000022042 14507372607 0024344 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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.
//go:build windows
// +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-20231004.02/google_guest_agent/agentcrypto/ 0000775 0000000 0000000 00000000000 14507372607 0022625 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/agentcrypto/credentials/ 0000775 0000000 0000000 00000000000 14507372607 0025122 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/agentcrypto/credentials/guest_credentials.pb.go 0000664 0000000 0000000 00000013631 14507372607 0031561 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.31.0
// protoc v3.21.12
// source: guest_credentials.proto
package credentials
import (
tpm "github.com/google/go-tpm-tools/proto/tpm"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// A server response containing client credentials.
type GuestCredentialsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
EncryptedCredentials []byte `protobuf:"bytes,1,opt,name=encrypted_credentials,json=encryptedCredentials,proto3" json:"encrypted_credentials,omitempty"`
KeyImportBlob *tpm.ImportBlob `protobuf:"bytes,2,opt,name=key_import_blob,json=keyImportBlob,proto3" json:"key_import_blob,omitempty"`
}
func (x *GuestCredentialsResponse) Reset() {
*x = GuestCredentialsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_guest_credentials_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GuestCredentialsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GuestCredentialsResponse) ProtoMessage() {}
func (x *GuestCredentialsResponse) ProtoReflect() protoreflect.Message {
mi := &file_guest_credentials_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GuestCredentialsResponse.ProtoReflect.Descriptor instead.
func (*GuestCredentialsResponse) Descriptor() ([]byte, []int) {
return file_guest_credentials_proto_rawDescGZIP(), []int{0}
}
func (x *GuestCredentialsResponse) GetEncryptedCredentials() []byte {
if x != nil {
return x.EncryptedCredentials
}
return nil
}
func (x *GuestCredentialsResponse) GetKeyImportBlob() *tpm.ImportBlob {
if x != nil {
return x.KeyImportBlob
}
return nil
}
var File_guest_credentials_proto protoreflect.FileDescriptor
var file_guest_credentials_proto_rawDesc = []byte{
0x0a, 0x17, 0x67, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x61, 0x67, 0x65, 0x6e, 0x74,
0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x73, 0x1a, 0x09, 0x74, 0x70, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x88, 0x01,
0x0a, 0x18, 0x47, 0x75, 0x65, 0x73, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61,
0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x15, 0x65, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x65, 0x6e, 0x63, 0x72, 0x79,
0x70, 0x74, 0x65, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12,
0x37, 0x0a, 0x0f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x6c,
0x6f, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x70, 0x6d, 0x2e, 0x49,
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x52, 0x0d, 0x6b, 0x65, 0x79, 0x49, 0x6d,
0x70, 0x6f, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x42, 0x0e, 0x5a, 0x0c, 0x2f, 0x63, 0x72, 0x65,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_guest_credentials_proto_rawDescOnce sync.Once
file_guest_credentials_proto_rawDescData = file_guest_credentials_proto_rawDesc
)
func file_guest_credentials_proto_rawDescGZIP() []byte {
file_guest_credentials_proto_rawDescOnce.Do(func() {
file_guest_credentials_proto_rawDescData = protoimpl.X.CompressGZIP(file_guest_credentials_proto_rawDescData)
})
return file_guest_credentials_proto_rawDescData
}
var file_guest_credentials_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_guest_credentials_proto_goTypes = []interface{}{
(*GuestCredentialsResponse)(nil), // 0: agentcrypto.credentials.GuestCredentialsResponse
(*tpm.ImportBlob)(nil), // 1: tpm.ImportBlob
}
var file_guest_credentials_proto_depIdxs = []int32{
1, // 0: agentcrypto.credentials.GuestCredentialsResponse.key_import_blob:type_name -> tpm.ImportBlob
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_guest_credentials_proto_init() }
func file_guest_credentials_proto_init() {
if File_guest_credentials_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_guest_credentials_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GuestCredentialsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_guest_credentials_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_guest_credentials_proto_goTypes,
DependencyIndexes: file_guest_credentials_proto_depIdxs,
MessageInfos: file_guest_credentials_proto_msgTypes,
}.Build()
File_guest_credentials_proto = out.File
file_guest_credentials_proto_rawDesc = nil
file_guest_credentials_proto_goTypes = nil
file_guest_credentials_proto_depIdxs = nil
}
guest-agent-20231004.02/google_guest_agent/agentcrypto/credentials/guest_credentials.proto 0000664 0000000 0000000 00000001667 14507372607 0031725 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 agentcrypto.credentials;
option go_package = "/credentials";
// Specify import path to "github.com/google/go-tpm-tools/proto/tpm.proto" during compilation.
import "tpm.proto";
// A server response containing client credentials.
message GuestCredentialsResponse {
bytes encrypted_credentials = 1;
tpm.ImportBlob key_import_blob = 2;
} guest-agent-20231004.02/google_guest_agent/agentcrypto/crypto_util.go 0000664 0000000 0000000 00000006671 14507372607 0025543 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 agentcrypto
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"github.com/google/tink/go/aead/subtle"
)
// parseCertificate validates certificate is in valid PEM format.
func parseCertificate(cert []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(cert)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM certificate")
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}
return x509Cert, nil
}
// parsePvtKey validates the key is in valid format and returns the EC Private Key.
func parsePvtKey(pemKey []byte) (*ecdsa.PrivateKey, error) {
key, _ := pem.Decode(pemKey)
if key == nil {
return nil, fmt.Errorf("failed to decode PEM Key")
}
ecKey, err := x509.ParseECPrivateKey(key.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse EC Private Key: %w", err)
}
return ecKey, nil
}
// serialNumber reads the certificate from file and returns the serial number in hex.
func serialNumber(f string) (string, error) {
d, err := os.ReadFile(f)
if err != nil {
return "", fmt.Errorf("unable to read previous client credential file %q: %w", f, err)
}
crt, err := parseCertificate(d)
if err != nil {
return "", fmt.Errorf("unable to parse certificate at %q: %w", f, err)
}
return fmt.Sprintf("%x", crt.SerialNumber), nil
}
// verifySign verifies the client certificate is valid and signed by root CA.
func verifySign(cert []byte, rootCAFile string) error {
caCertPEM, err := os.ReadFile(rootCAFile)
if err != nil {
return fmt.Errorf("failed to read CA PEM file for verifying signature: %w", err)
}
x509Cert, err := parseCertificate(cert)
if err != nil {
return fmt.Errorf("failed to parse client certificate for verifying signature: %w", err)
}
roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM(caCertPEM) {
return fmt.Errorf("failed to add %q to new certpool for verifying client certificate", rootCAFile)
}
opts := x509.VerifyOptions{
Roots: roots,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
if _, err := x509Cert.Verify(opts); err != nil {
return fmt.Errorf("failed to verify client certificate against root CA %q: %w", rootCAFile, err)
}
return nil
}
// encrypt encrypts plain text using AES GCM algorithm.
func encrypt(aesKey []byte, plainText []byte, associatedData []byte) ([]byte, error) {
cipher, err := subtle.NewAESGCM(aesKey)
if err != nil {
return nil, fmt.Errorf("failed to initialize cipher: %v", err)
}
return cipher.Encrypt(plainText, associatedData)
}
// decrypt decrypts AES GCM encrypted cipher text.
func decrypt(aesKey []byte, cipherText []byte, associatedData []byte) ([]byte, error) {
cipher, err := subtle.NewAESGCM(aesKey)
if err != nil {
return nil, fmt.Errorf("failed to initialize cipher: %v", err)
}
return cipher.Decrypt(cipherText, associatedData)
}
guest-agent-20231004.02/google_guest_agent/agentcrypto/crypto_util_test.go 0000664 0000000 0000000 00000022317 14507372607 0026575 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 agentcrypto
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"os"
"path/filepath"
"testing"
)
const validCertPEM = `
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw
WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp
bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q
5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC
7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAa
BgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUF
BwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcy
LmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2Nz
cDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAf
BgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisG
AQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29t
L0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+
gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283
TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq
0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoW
RvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOh
yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==
-----END CERTIFICATE-----`
const invalidCertPEM = `
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw
gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283
TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq
yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==
-----END CERTIFICATE-----`
func TestParseCertificate(t *testing.T) {
if _, err := parseCertificate([]byte(validCertPEM)); err != nil {
t.Errorf("parseCertificate(%s) failed unexpectedly with error: %v", validCertPEM, err)
}
}
func TestParseCertificateError(t *testing.T) {
if _, err := parseCertificate([]byte(invalidCertPEM)); err == nil {
t.Errorf("parseCertificate(%s) succeeded unexpectedly for invalid certificate, want error", invalidCertPEM)
}
}
func TestEncryptDecrypt(t *testing.T) {
// 32 byte key.
key := []byte("AES256Key-32Characters1234567890")
plaintext := []byte("testplaintext")
ciphertext, err := encrypt(key, plaintext, nil)
if err != nil {
t.Errorf("encrypt(%s,%s) failed unexpectedly with error: %v", key, plaintext, err)
}
got, err := decrypt(key, ciphertext, nil)
if err != nil {
t.Errorf("decrypt(%s,%s) failed unexpectedly with error: %v", string(key), string(ciphertext), err)
}
if !bytes.Equal(got, plaintext) {
t.Errorf("decrypt(%s,%s) = %s want %s", string(key), string(ciphertext), string(got), string(plaintext))
}
}
const cacert = `
-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIUFTF0rnA2LoffIJEKSh+rQcmehSIwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzA3MjgyMjI3MTdaGA8zMDIy
MTEyODIyMjcxN1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKiWs/hXZgTtFkpFvdXO/nLpLJSCq5rwqAJauTmj
Y78Za1QmgaqCcguakKf/hb+MxRL9h9qJVBAQkNZv0nChoTJyD6YF5hh4DDrQCPuh
1wvVsUhUllIbKsJbjQmdkOb3A5fMoe1ki4BLsr1CtJfJVj1+ifR+7hNkD3fW2sls
XZlrNZRmbMKq84KRBWTSSxhjYZGd2cCGpecJ2fWuva9QhairdnB4TORAfjiyH+5v
GEwXWC9gyDIIXWDG/kxwDDnh7kub0UsMf/neLv0hejpW/pfmvt32IoMaTEGFDaj7
lhTo7UVQw/XCFWqElsi8gHXR+/UdzbON5a8GiyjWJq5SThsCAwEAAaNTMFEwHQYD
VR0OBBYEFPvD/mUJgRgzLmWCD5zFNglzMb55MB8GA1UdIwQYMBaAFPvD/mUJgRgz
LmWCD5zFNglzMb55MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
ABUr0RNasEZ39wM1CDE/qZDo+gBMGWH8gE/x152KPvzvJZmI96LkYuKzmbIrvogJ
rfGYkAP2LYc8bX6zs4e2VycF0pml7ARKHyinzDdcwXOKzg9gGanoZw4wXEtxfWSl
GbmNplmhmMpEnrtTNeDbqGWvmO/1fziNduimNVVu1iltNYEszE/ch8AlMT7flfNm
JnhzvUUnGeXDiWUIJdneDfXopatOboL/0HimnfNK6//NKUlMCQOfNbNND+372jhK
B3V0o4sGyoh8/Jlas+SqEtVKv+jfNfAG0urLzJc4Zn2uc2chpZnD8DxkmzA5nJCf
+5xLOukYO2I5KMgyYkYNUXs=
-----END CERTIFICATE-----
`
func TestVerifySign(t *testing.T) {
// Fake self signed ceritificates for testing.
client := `
-----BEGIN CERTIFICATE-----
MIIDADCCAegCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAgFw0yMzA3MjgyMjI3NDZaGA8zMDIyMTEyODIyMjc0NlowRTELMAkGA1UEBhMC
QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
dHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxAeYva
w0J52P9e2IXPEyyJncOKOSGxCWqf0yHpuQ95STMMrgVSodN1Jdrpd2DPOqOYIriK
uHw1L4DCm5/yGP7WznN/JOORoJTZ5qJXBXNNQZxf1d5qJeBWtFnVv2pAPwFM/c8j
YNFCjTxAHjEMfZN0uXt1ELa6OkYCwxxiVq+Z6QT47xhvQHBzCFhPCaXy8ezvBanU
m2AJ2O3HYu9JCy37baDsyVlhrt1qRTKG3JFCgqGEs2vkFo25ebv0Nq8crtT8J6wz
YWbIpB56v+299f7jqStjljapG+nMrSbk8BRvMPAlg8Hg6mJ3RQgW5DgKE/7BSgue
U2oF7ODsKjF5xesCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAnbriOFcw2b/1zqfr
M3FK3TAjcD+InKpY/bNjhdbfCRgO3WYdnWsVU437vKkiJH0tAOAzdR3Yd5xpLkuS
uUjIiY4VTR10tZxmFuyAq87NZx4zMCJ7XNRQtDU5o+EUyZVV7l2fwbS31unCZQn5
10nSg5TQE3kZ4u+3x4PZgSbMhIY8P5Q/ZjAKVKk0hnT1ClQ5LQwetcDR7KMq9DpE
sC6BD/ElCi0RJrZqVVccAhumf9NBk/qWf1E4njlmYLmqNrfZGEfbxiKAOsZFYaCV
p/45VCE10OS3mYEFwJmQjH5NoqaSGxWU28reovEEmrDFoGYfkMQbxZzay0LURXt7
aSrX4A==
-----END CERTIFICATE-----
`
root := filepath.Join(t.TempDir(), "root.ca")
if err := os.WriteFile(root, []byte(cacert), 0644); err != nil {
t.Fatalf("Failed to setup test CA cert file: %v", err)
}
if err := verifySign([]byte(client), root); err != nil {
t.Errorf("verifySign failed unexpectedly with error: %v", err)
}
}
func TestVerifySignError(t *testing.T) {
// Fake invalid self signed ceritificates for testing.
client := `
-----BEGIN CERTIFICATE-----
MIIDADCCAegCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAgFw0yMzA3MjgyMjM4NTBaGA8zMDIyMTEyODIyMzg1MFowRTELMAkGA1UEBhMC
QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
dHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKW78MTO
TO6F5/68B/e3qQYHRJ1OYv43+1U503fTnkQIyf1KtZvABmPIXmckDJAlTmtD8WQp
lKVxCtSJ0aNNwj2epFBo/CoO5gIuFWjjxkiTfneCDxTF4SxqzVzvNuT0JtsG/Ysd
2b2GCIhHbqM7YLCol6V++SSO+NTR2kUx6RQ+f4vvnKWfv2pRgl8jHhq29U71BKtY
k1rH6kd13QOl71IMY3E2SRB9rONe0/lgrVyaKKJto5a0WVDgrjZP4e+0lpvtD3jN
JOFcJYrrDHAdxjQMEqbT4b1+M/HEOwJMDI2nZAI2exDmN8R2Wburp7hKNeygA4AM
7x91qP9jNfmS/wUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfv4sxcTTu66KU1h2
ol2DY2JQSywsWY37cfrdL9D1u2sf/MSyAN+i6XcwG/WReoPS8jLFPWJBVHYFQOWt
OVw93lVfFlFfz1GojCiddGZxZTWLhKSVvnkRVuRlOD7ph6UjowTUe+JrK5bh/pT8
m+g/HmvC/0V5fgQFvtujjc3DkHzKk7HXj39OFsLVGvNDdI6f7+mdc7ib2qs5/uQt
T+CR3W1LK08doMc8/SG74Q1i8eU1/AcX1QK1SQqX/TBF8EpCDII8BMTBp/KPp6JV
GPQpdL4CXXRtVxz5wf/GuMKbgBe9nPh9bFoRrmH6B/LK9dckvZJG9wT7lzuCXZ3d
zBbQ2g==
-----END CERTIFICATE-----
`
root := filepath.Join(t.TempDir(), "root.ca")
if err := os.WriteFile(root, []byte(cacert), 0644); err != nil {
t.Fatalf("Failed to setup test CA cert file: %v", err)
}
tests := []struct {
name string
client string
}{
{
name: "invalid_signed_client",
client: client,
},
{
name: "incorrectly_formatted_client",
client: invalidCertPEM,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if err := verifySign([]byte(test.client), root); err == nil {
t.Errorf("verifySign succeeded unexpectedly for %s, want error", test.name)
}
})
}
}
func TestSerialNumber(t *testing.T) {
f := filepath.Join(t.TempDir(), "cert")
if err := os.WriteFile(f, []byte(validCertPEM), 0777); err != nil {
t.Errorf("Failed to create test cert file: %v", err)
}
want := "137d4565568f5d35"
got, err := serialNumber(f)
if err != nil {
t.Errorf("serialNumber(%s) failed unexpectedly with error: %v", f, err)
}
if got != want {
t.Errorf("serialNumber(%s) = %s, want %s", f, got, want)
}
}
func generatePrivateKey(t *testing.T) (*ecdsa.PrivateKey, []byte) {
t.Helper()
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
t.Fatalf("Failed to generate key: %v", err)
}
x509Encoded, err := x509.MarshalECPrivateKey(key)
if err != nil {
t.Fatalf("Failed to Marshal EC PrivateKey: %v", err)
}
return key, pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509Encoded})
}
func TestParseECPrivateKey(t *testing.T) {
key, pem := generatePrivateKey(t)
got, err := parsePvtKey(pem)
if err != nil {
t.Errorf("parsePvtKey(%s) failed unexpectedly with error: %v", string(pem), err)
}
if !key.Equal(got) {
t.Errorf("parsePvtKey(%s) parsed private key incorrectly", string(pem))
}
}
guest-agent-20231004.02/google_guest_agent/agentcrypto/mtls_mds.go 0000664 0000000 0000000 00000016676 14507372607 0025016 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 agentcrypto provides various cryptography related utility functions for Guest Agent.
package agentcrypto
import (
"context"
"fmt"
"path/filepath"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/uefi"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"github.com/google/go-tpm-tools/client"
"github.com/google/go-tpm-tools/proto/tpm"
"github.com/google/go-tpm/legacy/tpm2"
"google.golang.org/protobuf/encoding/protojson"
pb "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/agentcrypto/credentials"
)
const (
// UEFI variables are of format {VariableName}-{VendorGUID}
// googleGUID is Google's (vendors/variable owners) GUID used to prevent name collision with other vendors.
googleGUID = "8be4df61-93ca-11d2-aa0d-00e098032b8c"
// googleRootCACertEFIVarName is predefined string part of the UEFI variable name that holds Root CA cert.
googleRootCACertEFIVarName = "InstanceRootCACertificate"
// clientCertsKey is the metadata server key at which client identity certificate is exposed.
clientCertsKey = "instance/credentials/certs"
// MTLSSchedulerID is the identifier used by job scheduler.
MTLSSchedulerID = "MTLS_MDS_Credential_Boostrapper"
// MTLSScheduleInterval is interval at which credential bootstrapper runs.
MTLSScheduleInterval = 48 * time.Hour
)
var (
googleRootCACertUEFIVar = uefi.VariableName{Name: googleRootCACertEFIVarName, GUID: googleGUID}
)
// CredsJob implements job scheduler interface for generating/rotating credentials.
type CredsJob struct {
client metadata.MDSClientInterface
}
// New initializer new job.
func New() *CredsJob {
return &CredsJob{
client: metadata.New(),
}
}
// readRootCACert reads Root CA cert from UEFI variable.
func (j *CredsJob) readRootCACert(name uefi.VariableName) (*uefi.Variable, error) {
rootCACert, err := uefi.ReadVariable(name)
if err != nil {
return nil, fmt.Errorf("unable to read root CA cert file contents: %w", err)
}
if _, err := parseCertificate(rootCACert.Content); err != nil {
return nil, fmt.Errorf("unable to verify Root CA cert: %w", err)
}
logger.Infof("Successfully read root CA Cert from %+v", name)
return rootCACert, nil
}
// getClientCredentials fetches encrypted credentials from MDS and unmarshal it into GuestCredentialsResponse.
func (j *CredsJob) getClientCredentials(ctx context.Context) (*pb.GuestCredentialsResponse, error) {
creds, err := j.client.GetKey(ctx, clientCertsKey, nil)
if err != nil {
return nil, fmt.Errorf("unable to get client credentials from MDS: %w", err)
}
res := &pb.GuestCredentialsResponse{}
if err := protojson.Unmarshal([]byte(creds), res); err != nil {
return nil, fmt.Errorf("unable to unmarshal MDS response(%+v): %w", creds, err)
}
return res, nil
}
// extractKey decrypts the key cipher text (Key encryption Key encrypted Data Dencryption Key)
// through vTPM and returns the key (DEK) as plain text.
func (j *CredsJob) extractKey(importBlob *tpm.ImportBlob) ([]byte, error) {
rwc, err := tpm2.OpenTPM()
if err != nil {
return nil, fmt.Errorf("unable to open a channel to the TPM: %w", err)
}
defer rwc.Close()
ek, err := client.EndorsementKeyECC(rwc)
if err != nil {
return nil, fmt.Errorf("failed to load a key from TPM: %w", err)
}
defer ek.Close()
dek, err := ek.Import(importBlob)
if err != nil {
return nil, fmt.Errorf("failed to decrypt import blob: %w", err)
}
return dek, nil
}
// fetchClientCredentials fetches encrypted client credentials from MDS,
// extracts Key Encryption Key (KEK) from vTPM, decrypts the client credentials using KEK,
// and verifies that the certificate is signed by root CA.
func (j *CredsJob) fetchClientCredentials(ctx context.Context, rootCA string) ([]byte, error) {
resp, err := j.getClientCredentials(ctx)
if err != nil {
return []byte{}, err
}
dek, err := j.extractKey(resp.GetKeyImportBlob())
if err != nil {
return []byte{}, err
}
plaintext, err := decrypt(dek, resp.GetEncryptedCredentials(), nil)
if err != nil {
return []byte{}, err
}
if err := verifySign(plaintext, rootCA); err != nil {
return []byte{}, err
}
return plaintext, nil
}
// Run generates the required credentials for MTLS MDS workflow.
//
// 1. Fetches, verifies and writes Root CA cert from UEFI variable to /run/google-mds-mtls/root.crt
// 2. Fetches encrypted client credentials from MDS, decrypts it via vTPM and writes it to /run/google-mds-mtls/client.key
//
// Note that these credentials are at `C:\Program Files\Google\Compute Engine\certs\mds` on Windows.
// Additionally agent also generates a PFX file on windows that can be used invoking HTTPS endpoint.
//
// Example usage of these credentials to call HTTPS endpoint of MDS:
//
// curl --cacert /run/google-mds-mtls/root.crt -E /run/google-mds-mtls/client.key -H "MetadataFlavor: Google" https://169.254.169.254
//
// Windows example:
//
// $cert = Get-PfxCertificate -FilePath "C:\ProgramData\Google\Compute Engine\mds-mtls-client.key.pfx"
// or
// $cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -like "*google.internal*" }
// Invoke-RestMethod -Uri https://169.254.169.254 -Method Get -Headers @{"Metadata-Flavor"="Google"} -Certificate $cert
func (j *CredsJob) Run(ctx context.Context) (bool, error) {
logger.Infof("Fetching Root CA cert...")
v, err := j.readRootCACert(googleRootCACertUEFIVar)
if err != nil {
return true, fmt.Errorf("failed to read Root CA cert with an error: %w", err)
}
if err := j.writeRootCACert(ctx, v.Content, filepath.Join(defaultCredsDir, rootCACertFileName)); err != nil {
return true, fmt.Errorf("failed to store Root CA cert with an error: %w", err)
}
logger.Infof("Fetching client credentials...")
creds, err := j.fetchClientCredentials(ctx, filepath.Join(defaultCredsDir, rootCACertFileName))
if err != nil {
return true, fmt.Errorf("failed to generate client credentials with an error: %w", err)
}
if err := j.writeClientCredentials(creds, filepath.Join(defaultCredsDir, clientCredsFileName)); err != nil {
return true, fmt.Errorf("failed to store client credentials with an error: %w", err)
}
logger.Infof("Successfully bootstrapped MDS mTLS credentials")
return true, nil
}
// ID returns the ID for this job.
func (j *CredsJob) ID() string {
return MTLSSchedulerID
}
// Interval returns the interval at which job is executed.
func (j *CredsJob) Interval() (time.Duration, bool) {
return MTLSScheduleInterval, true
}
// ShouldEnable returns true if MDS endpoint for fetching credentials is available on the VM.
// Used for identifying if we want schedule bootstrapping and enable MDS mTLS credential rotation.
func (j *CredsJob) ShouldEnable(ctx context.Context) bool {
_, err := j.client.GetKey(ctx, clientCertsKey, nil)
if err != nil {
logger.Warningf("Skipping scheduling credential generation job, failed to reach client credentials endpoint(%s) with error: %v", clientCertsKey, err)
return false
}
return true
}
guest-agent-20231004.02/google_guest_agent/agentcrypto/mtls_mds_linux.go 0000664 0000000 0000000 00000007222 14507372607 0026220 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 agentcrypto
import (
"context"
"fmt"
"os/exec"
"path/filepath"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"github.com/GoogleCloudPlatform/guest-agent/utils"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
const (
// defaultCredsDir is the directory location for MTLS MDS credentials.
defaultCredsDir = "/run/google-mds-mtls"
// rootCACertFileName is the root CA cert.
rootCACertFileName = "root.crt"
// clientCredsFileName are client credentials, its basically the file
// that has the EC private key and the client certificate concatenated.
clientCredsFileName = "client.key"
)
// writeRootCACert writes Root CA cert from UEFI variable to output file.
func (j *CredsJob) writeRootCACert(ctx context.Context, content []byte, outputFile string) error {
if err := utils.SaferWriteFile(content, outputFile, 0644); err != nil {
return err
}
// Best effort to update system store, don't fail.
if err := updateSystemStore(ctx, outputFile); err != nil {
logger.Errorf("Failed add Root MDS cert to system trust store with error: %v", err)
}
return nil
}
// writeClientCredentials stores client credentials (certificate and private key).
func (j *CredsJob) writeClientCredentials(plaintext []byte, outputFile string) error {
return utils.SaferWriteFile(plaintext, outputFile, 0644)
}
// getCAStoreUpdater interates over known system trust store updaters and returns the first found.
func getCAStoreUpdater() (string, error) {
knownUpdaters := []string{"update-ca-certificates", "update-ca-trust"}
var errs []string
for _, u := range knownUpdaters {
_, err := exec.LookPath(u)
if err == nil {
return u, nil
}
errs = append(errs, err.Error())
}
return "", fmt.Errorf("no known trust updaters %v were found: %v", knownUpdaters, errs)
}
// certificateDirFromUpdater returns directory of local CA certificates for the given updater tool.
func certificateDirFromUpdater(updater string) (string, error) {
switch updater {
// SUSE, Debian and Ubuntu distributions.
// https://manpages.ubuntu.com/manpages/xenial/man8/update-ca-certificates.8.html
case "update-ca-certificates":
return "/usr/local/share/ca-certificates/", nil
// CentOS, Fedora, RedHat distributions.
// https://www.unix.com/man-page/centos/8/UPDATE-CA-TRUST/
case "update-ca-trust":
return "/etc/pki/ca-trust/source/anchors/", nil
default:
return "", fmt.Errorf("unknown updater %q, no local trusted CA certificate directory found", updater)
}
}
// updateSystemStore updates the local system store with the cert.
func updateSystemStore(ctx context.Context, cert string) error {
cmd, err := getCAStoreUpdater()
if err != nil {
return err
}
dir, err := certificateDirFromUpdater(cmd)
if err != nil {
return err
}
dest := filepath.Join(dir, filepath.Base(cert))
if err := utils.CopyFile(cert, dest, 0644); err != nil {
return err
}
res := run.WithOutput(ctx, cmd)
if res.ExitCode != 0 {
return fmt.Errorf("command %q failed with error: %s", cmd, res.Error())
}
logger.Infof("Certificate %q added to system store successfully %s", cert, res.StdOut)
return nil
}
guest-agent-20231004.02/google_guest_agent/agentcrypto/mtls_mds_linux_test.go 0000664 0000000 0000000 00000011273 14507372607 0027260 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 agentcrypto
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/fakes"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/uefi"
)
func TestReadAndWriteRootCACert(t *testing.T) {
root := t.TempDir()
v := uefi.VariableName{Name: "testname", GUID: "testguid", RootDir: root}
j := &CredsJob{}
fakeUefi := []byte("attr" + validCertPEM)
path := filepath.Join(root, "testname-testguid")
if err := os.WriteFile(path, fakeUefi, 0644); err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
defer os.Remove(path)
crt := filepath.Join(root, "root.crt")
ca, err := j.readRootCACert(v)
if err != nil {
t.Errorf("readRootCACert(%+v) failed unexpectedly with error: %v", v, err)
}
if err := j.writeRootCACert(context.Background(), ca.Content, crt); err != nil {
t.Errorf("writeRootCACert(%s, %s) failed unexpectedly with error: %v", string(ca.Content), crt, err)
}
got, err := os.ReadFile(crt)
if err != nil {
t.Errorf("Failed to read expected root cert file: %v", err)
}
if string(got) != validCertPEM {
t.Errorf("readAndWriteRootCACert(%+v, %s) = %s, want %s", v, crt, string(got), validCertPEM)
}
}
func TestReadAndWriteRootCACertError(t *testing.T) {
root := t.TempDir()
v := uefi.VariableName{Name: "not", GUID: "exist", RootDir: root}
j := &CredsJob{}
// Non-existent UEFI variable.
if _, err := j.readRootCACert(v); err == nil {
t.Errorf("readRootCACert(%+v) succeeded unexpectedly for non-existent UEFI variable, want error", v)
}
// Invalid PEM certificate.
fakeUefi := []byte("attr" + invalidCertPEM)
path := filepath.Join(root, "testname-testguid")
if err := os.WriteFile(path, fakeUefi, 0644); err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
defer os.Remove(path)
if _, err := j.readRootCACert(v); err == nil {
t.Errorf("readRootCACert(%+v) succeeded unexpectedly for invalid PEM certificate, want error", v)
}
}
func TestGetClientCredentials(t *testing.T) {
ctx := context.WithValue(context.Background(), fakes.MDSOverride, "succeed")
j := &CredsJob{
client: fakes.NewFakeMDSClient(),
}
if _, err := j.getClientCredentials(ctx); err != nil {
t.Errorf("getClientCredentials(ctx, client) failed unexpectedly with error: %v", err)
}
}
func TestGetClientCredentialsError(t *testing.T) {
ctx := context.Background()
j := &CredsJob{
client: fakes.NewFakeMDSClient(),
}
tests := []string{"fail_mds_connect", "fail_unmarshal"}
for _, test := range tests {
t.Run(test, func(t *testing.T) {
ctx = context.WithValue(ctx, fakes.MDSOverride, test)
if _, err := j.getClientCredentials(ctx); err == nil {
t.Errorf("getClientCredentials(ctx, client) succeeded for %s, want error", test)
}
})
}
}
func TestShouldEnable(t *testing.T) {
ctx := context.WithValue(context.Background(), fakes.MDSOverride, "succeed")
j := &CredsJob{
client: fakes.NewFakeMDSClient(),
}
if !j.ShouldEnable(ctx) {
t.Error("ShouldEnable(ctx) = false, want true")
}
}
func TestShouldEnableError(t *testing.T) {
ctx := context.WithValue(context.Background(), fakes.MDSOverride, "fail_mds_connect")
j := &CredsJob{
client: fakes.NewFakeMDSClient(),
}
if j.ShouldEnable(ctx) {
t.Error("ShouldEnable(ctx) = true, want false")
}
}
func TestCertificateDirFromUpdater(t *testing.T) {
tests := []struct {
updater string
want string
}{
{
updater: "update-ca-certificates",
want: "/usr/local/share/ca-certificates/",
},
{
updater: "update-ca-trust",
want: "/etc/pki/ca-trust/source/anchors/",
},
}
for _, test := range tests {
t.Run(test.updater, func(t *testing.T) {
got, err := certificateDirFromUpdater(test.updater)
if err != nil {
t.Errorf("certificateDirFromUpdater(%s) failed unexpectedly with error: %v", test.updater, err)
}
if got != test.want {
t.Errorf("certificateDirFromUpdater(%s) = %s, want %s", test.updater, got, test.want)
}
})
}
}
func TestCertificateDirFromUpdaterError(t *testing.T) {
_, err := certificateDirFromUpdater("unknown")
if err == nil {
t.Errorf("certificateDirFromUpdater(unknown) succeeded for unknown updater, want error")
}
}
guest-agent-20231004.02/google_guest_agent/agentcrypto/mtls_mds_windows.go 0000664 0000000 0000000 00000023716 14507372607 0026561 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 agentcrypto
import (
"context"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"path/filepath"
"syscall"
"unsafe"
"github.com/GoogleCloudPlatform/guest-agent/utils"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"golang.org/x/sys/windows"
"software.sslmate.com/src/go-pkcs12"
)
const (
// rootCACertFileName is the root CA cert.
rootCACertFileName = "mds-mtls-root.crt"
// clientCredsFileName are client credentials, its basically the file
// that has the EC private key and the client certificate concatenated.
clientCredsFileName = "mds-mtls-client.key"
// pfxFile stores client credentials in PFX format.
pfxFile = "mds-mtls-client.key.pfx"
// https://learn.microsoft.com/en-us/windows/win32/seccrypto/system-store-locations
// my is predefined personal cert store.
my = "MY"
// root is predefined cert store for root trusted CA certs.
root = "ROOT"
// certificateIssuer is the issuer of client/root certificates for MDS mTLS.
certificateIssuer = "google.internal"
// maxCertEnumeration specifies the maximum number of times to search for a certificate
// with a serial number from a given issuer before giving up.
maxCertEnumeration = 5
)
var (
// defaultCredsDir is the directory location for MTLS MDS credentials.
defaultCredsDir = filepath.Join(os.Getenv("ProgramData"), "Google", "Compute Engine")
prevCtx *windows.CertContext
)
// writeRootCACert writes Root CA cert from UEFI variable to output file.
func (j *CredsJob) writeRootCACert(_ context.Context, cacert []byte, outputFile string) error {
if err := utils.SaferWriteFile(cacert, outputFile, 0644); err != nil {
return err
}
x509Cert, err := parseCertificate(cacert)
if err != nil {
return fmt.Errorf("failed to parse root CA cert: %w", err)
}
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certcreatecertificatecontext
certContext, err := windows.CertCreateCertificateContext(
windows.X509_ASN_ENCODING|windows.PKCS_7_ASN_ENCODING,
&x509Cert.Raw[0],
uint32(len(x509Cert.Raw)))
if err != nil {
return fmt.Errorf("CertCreateCertificateContext returned: %v", err)
}
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfreecertificatecontext
defer windows.CertFreeCertificateContext(certContext)
// Adds certificate to Root Trusted certificates.
if err := addCtxToLocalSystemStore(root, certContext, uint32(windows.CERT_STORE_ADD_REPLACE_EXISTING)); err != nil {
return fmt.Errorf("failed to store root cert ctx in store: %w", err)
}
return nil
}
// findCert finds and returns certificate issued by issuer with the serial number in the given the store.
func findCert(storeName, issuer, certID string) (*windows.CertContext, error) {
logger.Infof("Searching for certificate with serial number %s in store %s by issuer %s", certID, storeName, issuer)
st, err := windows.CertOpenStore(
windows.CERT_STORE_PROV_SYSTEM,
0,
0,
windows.CERT_SYSTEM_STORE_LOCAL_MACHINE,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(storeName))))
if err != nil {
return nil, fmt.Errorf("failed to open cert store: %w", err)
}
defer windows.CertCloseStore(st, 0)
// prev is used for enumerating through all the certificates that matches the issuer.
// On the first call to the function this parameter is NULL on all subsequent calls,
// this parameter is the last CertContext pointer returned by the CertFindCertificateInStore function
var prev *windows.CertContext
// maxCertEnumeration would avoid requiring a infinite loop that relies on enumerating
// until we get nil crt.
for i := 1; i <= maxCertEnumeration; i++ {
logger.Debugf("Attempt %d, searching certificate...", i)
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfindcertificateinstore
crt, err := windows.CertFindCertificateInStore(
st,
windows.X509_ASN_ENCODING|windows.PKCS_7_ASN_ENCODING,
0,
windows.CERT_FIND_ISSUER_STR,
unsafe.Pointer(syscall.StringToUTF16Ptr(issuer)),
prev)
if err != nil {
return nil, fmt.Errorf("unable to find certificate: %w", err)
}
if crt == nil {
return nil, fmt.Errorf("no certificate by issuer %s with ID %s", issuer, certID)
}
x509Cert, err := certContextToX509(crt)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate context: %w", err)
}
if fmt.Sprintf("%x", x509Cert.SerialNumber) == certID {
return crt, nil
}
prev = crt
}
return nil, nil
}
// writeClientCredentials stores client credentials (certificate and private key).
func (j *CredsJob) writeClientCredentials(creds []byte, outputFile string) error {
num, err := serialNumber(outputFile)
if err != nil {
logger.Warningf("Could not get previous serial number, will skip cleanup: %v", err)
}
if err := utils.SaferWriteFile(creds, outputFile, 0644); err != nil {
return fmt.Errorf("failed to write client key: %w", err)
}
pfx, err := generatePFX(creds)
if err != nil {
return fmt.Errorf("failed to generate PFX data from client credentials: %w", err)
}
p := filepath.Join(filepath.Dir(outputFile), pfxFile)
if err := utils.SaferWriteFile(pfx, p, 0644); err != nil {
return fmt.Errorf("failed to write PFX file: %w", err)
}
blob := windows.CryptDataBlob{
Size: uint32(len(pfx)),
Data: &pfx[0],
}
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-pfximportcertstore
handle, err := windows.PFXImportCertStore(&blob, syscall.StringToUTF16Ptr(""), windows.CRYPT_MACHINE_KEYSET)
if err != nil {
return fmt.Errorf("failed to import PFX in cert store: %w", err)
}
defer windows.CertCloseStore(handle, 0)
var crtCtx *windows.CertContext
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certenumcertificatesinstore
crtCtx, err = windows.CertEnumCertificatesInStore(handle, crtCtx)
if err != nil {
return fmt.Errorf("failed to get cert context for PFX from store: %w", err)
}
defer windows.CertFreeCertificateContext(crtCtx)
// Add certificate to personal store.
if err := addCtxToLocalSystemStore(my, crtCtx, uint32(windows.CERT_STORE_ADD_NEWER)); err != nil {
return fmt.Errorf("failed to store pfx cert context: %w", err)
}
// Search for previous certificate if its not already in memory.
if prevCtx == nil && num != "" {
prevCtx, err = findCert(my, certificateIssuer, num)
if err != nil {
logger.Warningf("Failed to find previous certificate with error: %v", err)
}
}
// Remove previous certificate only after successful refresh.
if err := deleteCert(prevCtx, my); err != nil {
logger.Warningf("Failed to delete previous certificate(%s) with error: %v", num, err)
}
prevCtx = windows.CertDuplicateCertificateContext(crtCtx)
return nil
}
// certContextToX509 creates an x509 Certificate from a Windows cert context.
func certContextToX509(ctx *windows.CertContext) (*x509.Certificate, error) {
der := unsafe.Slice(ctx.EncodedCert, int(ctx.Length))
return x509.ParseCertificate(der)
}
// generatePFX accepts certificate concatenated with private key and generates a PFX out of it.
// https://learn.microsoft.com/en-us/windows-hardware/drivers/install/personal-information-exchange---pfx--files
func generatePFX(creds []byte) (pfxData []byte, err error) {
cert, key := pem.Decode(creds)
x509Cert, err := x509.ParseCertificate(cert.Bytes)
if err != nil {
return []byte{}, fmt.Errorf("failed to parse client certificate: %w", err)
}
ecpvt, err := parsePvtKey(key)
if err != nil {
return []byte{}, fmt.Errorf("failed to parse EC PrivateKey from client credentials: %w", err)
}
return pkcs12.Encode(rand.Reader, ecpvt, x509Cert, nil, "")
}
func addCtxToLocalSystemStore(storeName string, certContext *windows.CertContext, disposition uint32) error {
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certopenstore
// https://learn.microsoft.com/en-us/windows-hardware/drivers/install/local-machine-and-current-user-certificate-stores
// https://learn.microsoft.com/en-us/windows/win32/seccrypto/system-store-locations#cert_system_store_local_machine
st, err := windows.CertOpenStore(
windows.CERT_STORE_PROV_SYSTEM,
0,
0,
windows.CERT_SYSTEM_STORE_LOCAL_MACHINE,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(storeName))))
if err != nil {
return fmt.Errorf("failed to open cert store: %w", err)
}
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certclosestore
defer windows.CertCloseStore(st, 0)
// https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certaddcertificatecontexttostore
if err := windows.CertAddCertificateContextToStore(st, certContext, disposition, nil); err != nil {
return fmt.Errorf("failed to add certificate context to store: %w", err)
}
return nil
}
func deleteCert(crtCtx *windows.CertContext, storeName string) error {
if crtCtx == nil {
return nil
}
st, err := windows.CertOpenStore(
windows.CERT_STORE_PROV_SYSTEM,
0,
0,
windows.CERT_SYSTEM_STORE_LOCAL_MACHINE,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(storeName))))
if err != nil {
return fmt.Errorf("failed to open cert store: %w", err)
}
defer windows.CertCloseStore(st, 0)
var dlCtx *windows.CertContext
dlCtx, err = windows.CertFindCertificateInStore(
st,
windows.X509_ASN_ENCODING|windows.PKCS_7_ASN_ENCODING,
0,
windows.CERT_FIND_EXISTING,
unsafe.Pointer(crtCtx),
dlCtx,
)
if err != nil {
return fmt.Errorf("unable to find the certificate in %q store to delete: %w", storeName, err)
}
return windows.CertDeleteCertificateFromStore(dlCtx)
}
guest-agent-20231004.02/google_guest_agent/cfg/ 0000775 0000000 0000000 00000000000 14507372607 0021025 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/cfg/cfg.go 0000664 0000000 0000000 00000025752 14507372607 0022126 0 ustar 00root root 0000000 0000000 // Copyright 2023 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 cfg is package responsible to loading and accessing the guest environment configuration.
package cfg
import (
"fmt"
"runtime"
"github.com/go-ini/ini"
)
var (
// instance is the single instance of configuration sections, once loaded this package
// should always return it.
instance *Sections
// dataSource is a pointer to a data source loading/defining function, unit tests will
// want to change this pointer to whatever makes sense to its implementation.
dataSources = defaultDataSources
)
const (
winConfigPath = `C:\Program Files\Google\Compute Engine\instance_configs.cfg`
unixConfigPath = `/etc/default/instance_configs.cfg`
defaultConfig = `
[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
reuse_homedir = false
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
[IpForwarding]
ethernet_proto_id = 66
ip_aliases = true
target_instance_ips = true
[Instance]
instance_id =
instance_id_dir = /etc/google_instance_id
[InstanceSetup]
host_key_dir = /etc/ssh
host_key_types = ecdsa,ed25519,rsa
network_enabled = true
optimize_local_ssd = true
set_boto_config = true
set_host_keys = true
set_multiqueue = true
[MetadataScripts]
default_shell = /bin/bash
run_dir =
shutdown = true
shutdown-windows = true
startup = true
startup-windows = true
sysprep-specialize = true
[NetworkInterfaces]
dhcp_command =
ip_forwarding = true
setup = true
[OSLogin]
cert_authentication = true
[Snapshots]
enabled = false
snapshot_service_ip = 169.254.169.254
snapshot_service_port = 8081
timeout_in_seconds = 60
[Unstable]
mds_mtls = false
`
)
// Sections encapsulates all the configuration sections.
type Sections struct {
// AccountManager defines the address management configurations. It takes precedence over instance's
// and project's metadata configuration. The default configuration doesn't define values to it, if the
// user has defined it then we shouldn't even consider metadata values. Users must check if this
// pointer is nil or not.
AccountManager *AccountManager `ini:"accountManager,omitempty"`
// Accounts defines the non windows account management options, behaviors and commands.
Accounts *Accounts `ini:"Accounts,omitempty"`
// AddressManager defines the address management configurations. It takes precedence over instance's
// and project's metadata configuration. The default configuration doesn't define values to it, if the
// user has defined it then we shouldn't even consider metadata values. Users must check if this
// pointer is nil or not.
AddressManager *AddressManager `ini:"addressManager,omitempty"`
// Daemons defines the availability of clock skew, network and account managers.
Daemons *Daemons `ini:"Daemons,omitempty"`
// Diagnostics defines the diagnostics configurations. It takes precedence over instance's
// and project's metadata configuration. The default configuration doesn't define values to it, if the
// user has defined it then we shouldn't even consider metadata values. Users must check if this
// pointer is nil or not.
Diagnostics *Diagnostics `ini:"diagnostics,omitempty"`
// IPForwarding defines the ip forwarding configuration options.
IPForwarding *IPForwarding `ini:"IpForwarding,omitempty"`
// Instance defines the instance ID handling behaviors, i.e. where to read the ID from etc.
Instance *Instance `ini:"Instance,omitempty"`
// InstanceSetup defines options to basic instance setup options i.e. optimize local ssd, network,
// host keys etc.
InstanceSetup *InstanceSetup `ini:"InstanceSetup,omitempty"`
// MetadataScripts contains the configurations of the metadata-scripts service.
MetadataScripts *MetadataScripts `ini:"MetadataScripts,omitempty"`
// NetworkInterfaces defines if the network interfaces should be managed/configured by guest-agent
// as well as the commands definitions for network configuration.
NetworkInterfaces *NetworkInterfaces `ini:"NetworkInterfaces,omitempty"`
// OSLogin defines the OS Login configuration options.
OSLogin *OSLogin `ini:"OSLogin,omitempty"`
// Snpashots defines the snapshot listener configuration and behavior i.e. the server address and port.
Snapshots *Snapshots `ini:"Snapshots,omitempty"`
// Unstable is a "under development feature flags" section. No stability or long term support is
// guaranteed for any keys under this section. No application, script or utility should rely on it.
Unstable *Unstable `ini:"Unstable,omitempty"`
// WSFC defines the wsfc configurations. It takes precedence over instance's and project's
// metadata configuration. The default configuration doesn't define values to it, if the user
// has defined it then we shouldn't even consider metadata values. Users must check if this
// pointer is nil or not.
WSFC *WSFC `ini:"wsfc,omitempty"`
}
// AccountManager contains the configurations of AccountManager section.
type AccountManager struct {
Disable bool `ini:"disable,omitempty"`
}
// Accounts contains the configurations of Accounts section.
type Accounts struct {
DeprovisionRemove bool `ini:"deprovision_remove,omitempty"`
GPasswdAddCmd string `ini:"gpasswd_add_cmd,omitempty"`
GPasswdRemoveCmd string `ini:"gpasswd_remove_cmd,omitempty"`
GroupAddCmd string `ini:"groupadd_cmd,omitempty"`
Groups string `ini:"groups,omitempty"`
ReuseHomedir bool `ini:"reuse_homedir,omitempty"`
UserAddCmd string `ini:"useradd_cmd,omitempty"`
UserDelCmd string `ini:"userdel_cmd,omitempty"`
}
// AddressManager contains the configuration of addressManager section.
type AddressManager struct {
Disable bool `ini:"disable,omitempty"`
}
// Daemons contains the configurations of Daemons section.
type Daemons struct {
AccountsDaemon bool `ini:"accounts_daemon,omitempty"`
ClockSkewDaemon bool `ini:"clock_skew_daemon,omitempty"`
NetworkDaemon bool `ini:"network_daemon,omitempty"`
}
// Diagnostics contains the configurations of Diagnostics section.
type Diagnostics struct {
Enable bool `ini:"enable,omitempty"`
}
// IPForwarding contains the configurations of IPForwarding section.
type IPForwarding struct {
EthernetProtoID string `ini:"ethernet_proto_id,omitempty"`
IPAliases bool `ini:"ip_aliases,omitempty"`
TargetInstanceIPs bool `ini:"target_instance_ips,omitempty"`
}
// Instance contains the configurations of Instance section.
type Instance struct {
// InstanceID is a backward compatible key. In the past the instance id was only
// supported/setup via config file, if we can't read the instance_id file then
// try honoring this configuration key.
InstanceID string `ini:"instance_id,omitempty"`
// InstanceIDDir defines where the instance id file should be read from.
InstanceIDDir string `ini:"instance_id_dir,omitempty"`
}
// InstanceSetup contains the configurations of InstanceSetup section.
type InstanceSetup struct {
HostKeyDir string `ini:"host_key_dir,omitempty"`
HostKeyTypes string `ini:"host_key_types,omitempty"`
NetworkEnabled bool `ini:"network_enabled,omitempty"`
OptimizeLocalSSD bool `ini:"optimize_local_ssd,omitempty"`
SetBotoConfig bool `ini:"set_boto_config,omitempty"`
SetHostKeys bool `ini:"set_host_keys,omitempty"`
SetMultiqueue bool `ini:"set_multiqueue,omitempty"`
}
// MetadataScripts contains the configurations of MetadataScripts section.
type MetadataScripts struct {
DefaultShell string `ini:"default_shell,omitempty"`
RunDir string `ini:"run_dir,omitempty"`
Shutdown bool `ini:"shutdown,omitempty"`
ShutdownWindows bool `ini:"shutdown-windows,omitempty"`
Startup bool `ini:"startup,omitempty"`
StartupWindows bool `ini:"startup-windows,omitempty"`
SysprepSpecialize bool `ini:"sysprep_specialize,omitempty"`
}
// OSLogin contains the configurations of OSLogin section.
type OSLogin struct {
CertAuthentication bool `ini:"cert_authentication,omitempty"`
}
// NetworkInterfaces contains the configurations of NetworkInterfaces section.
type NetworkInterfaces struct {
DHCPCommand string `ini:"dhcp_command,omitempty"`
IPForwarding bool `ini:"ip_forwarding,omitempty"`
Setup bool `ini:"setup,omitempty"`
}
// Snapshots contains the configurations of Snapshots section.
type Snapshots struct {
Enabled bool `ini:"enabled,omitempty"`
SnapshotServiceIP string `ini:"snapshot_service_ip,omitempty"`
SnapshotServicePort int `ini:"snapshot_service_port,omitempty"`
TimeoutInSeconds int `ini:"timeout_in_seconds,omitempty"`
}
// Unstable contains the configurations of Unstable section. No long term stability or support
// is guaranteed for configurations defined in the Unstable section. By default all flags defined
// in this section is disabled and is intended to isolate under development features.
type Unstable struct {
MDSMTLS bool `ini:"mds_mtls,omitempty"`
}
// WSFC contains the configurations of WSFC section.
type WSFC struct {
Addresses string `ini:"addresses,omitempty"`
Enable bool `ini:"enable,omitempty"`
Port string `ini:"port,omitempty"`
}
func defaultConfigFile(osName string) string {
if osName == "windows" {
return winConfigPath
}
return unixConfigPath
}
func defaultDataSources(extraDefaults []byte) []interface{} {
var res []interface{}
configFile := defaultConfigFile(runtime.GOOS)
if len(extraDefaults) > 0 {
res = append(res, extraDefaults)
}
return append(res, []interface{}{
[]byte(defaultConfig),
configFile,
configFile + ".distro",
configFile + ".template",
}...)
}
// Load loads default configuration and the configuration from default config files.
func Load(extraDefaults []byte) error {
opts := ini.LoadOptions{
Loose: true,
Insensitive: true,
}
sources := dataSources(extraDefaults)
cfg, err := ini.LoadSources(opts, sources[0], sources[1:]...)
if err != nil {
return fmt.Errorf("failed to load configuration: %+v", err)
}
sections := new(Sections)
if err := cfg.MapTo(sections); err != nil {
return fmt.Errorf("failed to map configuration to object: %+v", err)
}
instance = sections
return nil
}
// Get returns the configuration's instance previously loaded with Load().
func Get() *Sections {
if instance == nil {
panic("cfg package was not initialized, Load() " +
"should be called in the early initialization code path")
}
return instance
}
guest-agent-20231004.02/google_guest_agent/cfg/cfg_test.go 0000664 0000000 0000000 00000005213 14507372607 0023153 0 ustar 00root root 0000000 0000000 // Copyright 2023 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 cfg
import "testing"
func TestLoad(t *testing.T) {
if err := Load(nil); err != nil {
t.Fatalf("Failed to load configuration: %+v", err)
}
cfg := Get()
if cfg.WSFC != nil {
t.Errorf("WSFC shouldn't not be defined by default configuration, expected: nil, got: non-nil")
}
if cfg.Accounts.DeprovisionRemove == true {
t.Errorf("Expected Accounts.deprovision_remove to be: false, got: true")
}
}
func TestInvalidConfig(t *testing.T) {
invalidConfig := `
[Section
key = value
`
dataSources = func(extraDefaults []byte) []interface{} {
return []interface{}{
[]byte(invalidConfig),
}
}
// After testing set it back to the default one.
defer func() {
dataSources = defaultDataSources
}()
if err := Load(nil); err == nil {
t.Errorf("Load() didn't fail to load invalid configuration, expected error")
}
}
func TestDefaultDataSources(t *testing.T) {
expectedDataSources := 4
sources := defaultDataSources(nil)
if len(sources) != expectedDataSources {
t.Errorf("defaultDataSources() returned wrong number of sources, expected: %d, got: %d",
expectedDataSources, len(sources))
}
_, ok := sources[0].([]byte)
if !ok {
t.Errorf("defaultDataSources() returned wrong sources, first source should be of type []byte")
}
}
func TestDefaultConfigFile(t *testing.T) {
windowsConfig := `C:\Program Files\Google\Compute Engine\instance_configs.cfg`
unixConfig := `/etc/default/instance_configs.cfg`
if got := defaultConfigFile("windows"); got != windowsConfig {
t.Errorf("defaultConfigFile(windows) returned wrong file, expected: %s, got: %s", windowsConfig, got)
}
if got := defaultConfigFile("linux"); got != unixConfig {
t.Errorf("defaultConfigFile(linux) returned wrong file, expected: %s, got: %s", unixConfig, got)
}
}
func TestGetTwice(t *testing.T) {
if err := Load(nil); err != nil {
t.Fatalf("Failed to load configuration: %+v", err)
}
firstCfg := Get()
secondCfg := Get()
if firstCfg != secondCfg {
t.Errorf("Get() should return always the same pointer, expected: %p, got: %p", firstCfg, secondCfg)
}
}
guest-agent-20231004.02/google_guest_agent/clock.go 0000664 0000000 0000000 00000003643 14507372607 0021716 0 ustar 00root root 0000000 0000000 // Copyright 2019 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
// https://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"
"runtime"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
type clockskewMgr struct{}
func (a *clockskewMgr) Diff(ctx context.Context) (bool, error) {
return oldMetadata.Instance.VirtualClock.DriftToken != newMetadata.Instance.VirtualClock.DriftToken, nil
}
func (a *clockskewMgr) Timeout(ctx context.Context) (bool, error) {
return false, nil
}
func (a *clockskewMgr) Disabled(ctx context.Context) (bool, error) {
enabled := cfg.Get().Daemons.ClockSkewDaemon
return runtime.GOOS == "windows" || !enabled, nil
}
func (a *clockskewMgr) Set(ctx context.Context) error {
if runtime.GOOS == "freebsd" {
err := run.Quiet(ctx, "service", "ntpd", "status")
if err == nil {
if err := run.Quiet(ctx, "service", "ntpd", "stop"); err != nil {
return err
}
defer func() {
if err := run.Quiet(ctx, "service", "ntpd", "start"); err != nil {
logger.Warningf("Error starting 'ntpd' after clock sync: %v.", err)
}
}()
}
// TODO get server
return run.Quiet(ctx, "ntpdate", "169.254.169.254")
}
res := run.WithOutput(ctx, "/sbin/hwclock", "--hctosys", "-u", "--noadjfile")
if res.ExitCode != 0 || res.StdErr != "" {
return error(res)
}
return nil
}
guest-agent-20231004.02/google_guest_agent/diagnostics.go 0000664 0000000 0000000 00000007576 14507372607 0023143 0 ustar 00root root 0000000 0000000 // Copyright 2018 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
// https://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"
"reflect"
"runtime"
"sync/atomic"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"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
}
type diagnosticsMgr struct {
// fakeWindows forces Disabled to run as if it was running in a windows system.
// mostly target for unit tests.
fakeWindows bool
}
func (d *diagnosticsMgr) Diff(ctx context.Context) (bool, error) {
return !reflect.DeepEqual(newMetadata.Instance.Attributes.Diagnostics, oldMetadata.Instance.Attributes.Diagnostics), nil
}
func (d *diagnosticsMgr) Timeout(ctx context.Context) (bool, error) {
return false, nil
}
func (d *diagnosticsMgr) Disabled(ctx context.Context) (bool, error) {
var disabled bool
config := cfg.Get()
if !d.fakeWindows && runtime.GOOS != "windows" {
return true, nil
}
defer func() {
if disabled != diagnosticsDisabled {
diagnosticsDisabled = disabled
logStatus("diagnostics", disabled)
}
}()
// Diagnostics are opt-in and enabled by default.
if config.Diagnostics != nil {
return !config.Diagnostics.Enable, nil
}
if newMetadata.Instance.Attributes.EnableDiagnostics != nil {
return !*newMetadata.Instance.Attributes.EnableDiagnostics, nil
}
if newMetadata.Project.Attributes.EnableDiagnostics != nil {
return !*newMetadata.Project.Attributes.EnableDiagnostics, nil
}
return diagnosticsDisabled, nil
}
func (d *diagnosticsMgr) Set(ctx context.Context) 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
}
expired, _ := utils.CheckExpired(entry.ExpireOn)
if entry.SignedURL == "" || 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
}
go func() {
logger.Infof("Diagnostics: collecting logs from the system.")
res := run.WithCombinedOutput(ctx, diagnosticsCmd, args...)
logger.Infof(res.Combined)
if res.ExitCode != 0 {
logger.Warningf("Error collecting logs: %v", res.Error())
}
// Job is done, unblock the following requests
atomic.SwapInt32(&isDiagnosticsRunning, 0)
}()
return writeRegMultiString(regKeyBase, diagnosticsRegKey, diagnosticsEntries)
}
guest-agent-20231004.02/google_guest_agent/diagnostics_test.go 0000664 0000000 0000000 00000006746 14507372607 0024200 0 ustar 00root root 0000000 0000000 // Copyright 2018 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
// https://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"
"testing"
"time"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-agent/utils"
)
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}
expired, _ := utils.CheckExpired(k.ExpireOn)
if tt.e != 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.Descriptor
want bool
}{
{"not explicitly enabled", []byte(""), &metadata.Descriptor{}, false},
{"enabled in cfg only", []byte("[diagnostics]\nenable=true"), &metadata.Descriptor{}, false},
{"disabled in cfg only", []byte("[diagnostics]\nenable=false"), &metadata.Descriptor{}, true},
{"disabled in cfg, enabled in instance metadata", []byte("[diagnostics]\nenable=false"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(true)}}}, true},
{"enabled in cfg, disabled in instance metadata", []byte("[diagnostics]\nenable=true"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(false)}}}, false},
{"enabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(true)}}}, false},
{"enabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(true)}}}, false},
{"disabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(false)}}}, true},
{"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(true)}}, Project: metadata.Project{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(false)}}}, false},
{"disabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{EnableDiagnostics: mkptr(false)}}}, true},
}
ctx := context.Background()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reloadConfig(t, tt.data)
newMetadata = tt.md
mgr := diagnosticsMgr{
fakeWindows: true,
}
got, err := mgr.Disabled(ctx)
if err != nil {
t.Errorf("Failed to run diagnosticsMgr's Disable() call: %+v", err)
}
if got != tt.want {
t.Errorf("test case %q, diagnostics.disabled() got: %t, want: %t", tt.name, got, tt.want)
}
})
}
}
guest-agent-20231004.02/google_guest_agent/events/ 0000775 0000000 0000000 00000000000 14507372607 0021572 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/events/Readme.md 0000664 0000000 0000000 00000015400 14507372607 0023311 0 ustar 00root root 0000000 0000000 # Guest Agent Events Handling Layer
## Overview
The Guest Agent events handling layer is a generic and multi-purpose events handling layer, it's designed to offer a unified mechanism and API to manage and communicate "external" events across the guest agent implementation. Such "external" events could be a underlying Operating System event such as file changes or removal, sockets, named pipes etc as well as platform services such as metadata service, snapshot service etc.
The events layer is formed of a **Manager**, a **Watcher** and a **Subscriber** where the **Manager** is the events controller/manager itself, the **Watcher** is the implementation of the event listening and the **Subscriber** is the callback function interested in a given event and registered to handle it or "to be notified when they happen".
Each **Event** is internally identified by a string ID, when registering the **Subscriber** must tell what event it's interested on, such as:
```golang
eventManager.Subscribe("metadata-watcher,longpoll", &userData, func(evType string, data interface{}, evData interface{}) bool {
// Event handling implementation...
return true
})
```
The **Subscriber** implementation must return a boolean, such a boolean determines if the **Subscriber** must be renewed or if it must be unregistered/unsubscribed.
## Sequence Diagram
Below is a high level sequence diagram showing how the **Guest Agent**, **Manager**, **Watchers** and **Handlers/Subscribers** interact with each other:
```
┌───────────┐ ┌─────────────┐ ┌─────────┐┌─────────┐┌─────────┐┌─────────┐
│Guest Agent│ │Event Manager│ │Watcher A││Watcher B││Handler A││Handler B│
└─────┬─────┘ └──────┬──────┘ └────┬────┘└────┬────┘└────┬────┘└────┬────┘
│ │ │ │ │ │
│ Initialize │ │ │ │ │
│────────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Register │ │ │ │
│ │─────────────>│ │ │ │
│ │ │ │ │ │
│ │ Register │ │ │
│ │────────────────────────>│ │ │
│ │ │ │ │ │
│Done initializing│ │ │ │ │
│<────────────────│ │ │ │ │
│ │ │ │ │ │
│ │ Subscribe()│ │ │ │
│─────────────────────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ Subscribe() │ │ │
│────────────────────────────────────────────────────────────────>│
│ │ │ │ │ │
│ Run() │ │ │ │ │
│────────────────>│ │ │ │ │
│ │ │ │ │ │
│ │ Run() │ │ │ │
│ │─────────────>│ │ │ │
│ │ │ │ │ │
│ │ Run() │ │ │
│ │────────────────────────>│ │ │
│ │ │ │ │ │
│ │dispatch event│ │ │ │
│ │<─────────────│ │ │ │
│ │ │ │ │ │
│ │ │Call() │ │ │
│ │───────────────────────────────────>│ │
│ │ │ │ │ │
│ │ dispatch event │ │ │
│ │<────────────────────────│ │ │
│ │ │ │ │ │
│ │ │ Call() │ │
│ │──────────────────────────────────────────────>│
┌─────┴─────┐ ┌──────┴──────┐ ┌────┴────┐┌────┴────┐┌────┴────┐┌────┴────┐
│Guest Agent│ │Event Manager│ │Watcher A││Watcher B││Handler A││Handler B│
└───────────┘ └─────────────┘ └─────────┘└─────────┘└─────────┘└─────────┘
```
## Built-in Watchers
|Watcher|Events|Desc|
|-------|------|----|
|metadata|metadata-watcher,longpoll|A new version of the metadata descriptor was detected.|
|ssh-trusted-ca-pipe-watcher|ssh-trusted-ca-pipe-watcher,read|A read in the trusted-ca pipe was detected.|
guest-agent-20231004.02/google_guest_agent/events/events.go 0000664 0000000 0000000 00000022376 14507372607 0023437 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 events is a events processing layer.
package events
import (
"context"
"fmt"
"sync"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/metadata"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/sshtrustedca"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
var (
// availableWatchers mapps all kown available event watchers.
availableWatchers = make(map[string]Watcher)
)
// Watcher defines the interface between the events manager and the actual
// watcher implementation.
type Watcher interface {
// ID returns the watcher id.
ID() string
// Events return a slice with all the event types a given Watcher handles.
Events() []string
// Run implements the actuall "listening" strategy and emits a event "signal".
// It must return:
// - [bool] if the watcher should renew(run again).
// - [interface{}] a event context data pointer further describing the event(if needed).
// - [err] error case the Watcher failed and wants to notify subscribers(see EventData).
Run(ctx context.Context, evType string) (bool, interface{}, error)
}
// Manager defines the interface between events management layer and the
// core guest agent implementation.
type Manager struct {
watchers map[string]Watcher
subscribers map[string][]*eventSubscriber
}
// Config offers a mechanism for the consumers to configure the Manager behavior.
type Config struct {
// Watchers lists the enabled watchers, of not provided all available watchers will be enabled.
Watchers []string
}
// EventData wraps the data communicated from a Watcher to a Subscriber.
type EventData struct {
// Data points to the Watcher provided data.
Data interface{}
// Error is used when a Watcher has failed and wants communicate its subscribers about the error.
Error error
}
// WatcherEventType wraps/couples together a Watcher and an event type.
type WatcherEventType struct {
// watcher is the watcher implementation for a given event type.
watcher Watcher
// evType idenfities the event type this object refences to.
evType string
}
// EventCb defines the callback interface between watchers and subscribers. The arguments are:
// - ctx the app' context passed in from the manager's Run() call.
// - evType a string defining the what event type triggered the call.
// - data a user context pointer to be consumed by the callback.
// - evData a event specific data pointer.
//
// The callback should return true if it wants to renew, returning false will case the callback
// to be unregistered/unsubscribed.
type EventCb func(ctx context.Context, evType string, data interface{}, evData *EventData) bool
type eventSubscriber struct {
data interface{}
cb EventCb
}
type eventBusData struct {
evType string
data *EventData
}
func init() {
err := initWatchers([]Watcher{
metadata.New(),
sshtrustedca.New(sshtrustedca.DefaultPipePath),
})
if err != nil {
logger.Errorf("Failed to initialize watchers: %+v", err)
}
}
// init initializes the known available event watchers.
func initWatchers(watchers []Watcher) error {
for _, curr := range watchers {
// Error if we are accidentaly not properly setting the id.
if curr.ID() == "" {
return fmt.Errorf("invalid event watcher id, skipping")
}
availableWatchers[curr.ID()] = curr
}
return nil
}
// New allocates and initializes a events Manager based on provided cfg.
func New(cfg *Config) (*Manager, error) {
res := &Manager{
watchers: availableWatchers,
subscribers: make(map[string][]*eventSubscriber),
}
// Align manager's config based on consumers provided watchers If it is
// passing in wanted/expected watchers, otherwise use all available ones.
if cfg != nil && len(cfg.Watchers) > 0 {
res.watchers = make(map[string]Watcher)
for _, curr := range cfg.Watchers {
// Report back if we don't know the provided watcher id.
if _, found := availableWatchers[curr]; !found {
return nil, fmt.Errorf("invalid/unknown watcher id: %s", curr)
}
res.watchers[curr] = availableWatchers[curr]
}
}
return res, nil
}
// Subscribe registers an event consumer/subscriber callback to a given event type, data
// is a context pointer provided by the caller to be passed down when calling cb when
// a new event happens.
func (mngr *Manager) Subscribe(evType string, data interface{}, cb EventCb) {
mngr.subscribers[evType] = append(mngr.subscribers[evType],
&eventSubscriber{
data: data,
cb: cb,
},
)
}
func (mngr *Manager) eventTypes() []*WatcherEventType {
var res []*WatcherEventType
for _, watcher := range mngr.watchers {
for _, evType := range watcher.Events() {
res = append(res, &WatcherEventType{watcher, evType})
}
}
return res
}
type watcherQueue struct {
mutex sync.Mutex
watchersMap map[string]bool
}
func (ep *watcherQueue) add(evType string) {
ep.mutex.Lock()
defer ep.mutex.Unlock()
ep.watchersMap[evType] = true
}
func (ep *watcherQueue) del(evType string) int {
ep.mutex.Lock()
defer ep.mutex.Unlock()
delete(ep.watchersMap, evType)
return len(ep.watchersMap)
}
// Run runs the event manager, it will block until all watchers have given up/failed.
func (mngr *Manager) Run(ctx context.Context) {
var wg sync.WaitGroup
var leaving bool
syncBus := make(chan eventBusData)
defer close(syncBus)
cancelContext := make(chan bool)
cancelCallback := make(chan bool)
defer close(cancelContext)
defer close(cancelCallback)
// Manages the context's done signal, pass it down to the other go routines to
// finish its job and leave. Additionally, if the remaining go routines are leaving
// we get it handled via syncBus channel and drop this go routine as well.
wg.Add(1)
go func(done <-chan struct{}, cancelContext <-chan bool, cancelCallback chan<- bool) {
defer wg.Done()
for {
select {
case <-done:
logger.Debugf("Got context's Done() signal, leaving.")
leaving = true
cancelCallback <- true
return
case <-cancelContext:
leaving = true
return
}
}
}(ctx.Done(), cancelContext, cancelCallback)
// Manages the event processing avoiding blocking the watcher's go routines.
// This will listen to syncBus and call the events handlers/callbacks.
wg.Add(1)
go func(bus <-chan eventBusData, cancelCallback <-chan bool) {
defer wg.Done()
for {
select {
case <-cancelCallback:
return
case busData := <-bus:
subscribers, found := mngr.subscribers[busData.evType]
if !found || len(subscribers) == 0 {
logger.Debugf("No subscriber found for event: %s, returning.", busData.evType)
continue
}
keepMe := make([]*eventSubscriber, 0)
for _, curr := range mngr.subscribers[busData.evType] {
logger.Debugf("Running registered callback for event: %s", busData.evType)
renew := curr.cb(ctx, busData.evType, curr.data, busData.data)
if renew {
keepMe = append(keepMe, curr)
}
logger.Debugf("Returning from event %q subscribed callback, should renew?: %t", busData.evType, renew)
}
mngr.subscribers[busData.evType] = keepMe
// No more subscribers for this event type, delete it from the subscribers map.
if len(keepMe) == 0 {
logger.Debugf("No more subscribers left for evType: %s", busData.evType)
delete(mngr.subscribers, busData.evType)
}
// No more subscribers at all, we have nothing more left to do here.
if len(mngr.subscribers) == 0 {
logger.Debugf("No subscribers left, leaving")
break
}
}
}
}(syncBus, cancelCallback)
// This control struct manages the registered watchers, when it gets to len()
// down to zero means all watchers are done and we can signal the other 2 control
// go routines to leave(given we don't have any more job left to process).
control := &watcherQueue{
watchersMap: make(map[string]bool),
}
// Creates a goroutine for each registered watcher's event and keep handling its
// execution until they give up/finishes their job by returning renew = false.
for _, curr := range mngr.eventTypes() {
control.add(curr.evType)
wg.Add(1)
go func(bus chan<- eventBusData, watcher Watcher, evType string, cancelContext chan<- bool, cancelCallback chan<- bool) {
var evData interface{}
var err error
defer wg.Done()
for renew := true; renew; {
renew, evData, err = watcher.Run(ctx, evType)
logger.Debugf("Watcher(%s) returned event: %q, should renew?: %t", watcher.ID(), evType, renew)
if leaving {
break
}
bus <- eventBusData{
evType: evType,
data: &EventData{
Data: evData,
Error: err,
},
}
}
if !leaving && control.del(evType) == 0 {
logger.Debugf("All watchers are finished, signaling to leave.")
cancelContext <- true
cancelCallback <- true
}
}(syncBus, curr.watcher, curr.evType, cancelContext, cancelCallback)
}
wg.Wait()
}
guest-agent-20231004.02/google_guest_agent/events/events_test.go 0000664 0000000 0000000 00000020724 14507372607 0024471 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 events
import (
"context"
"fmt"
"testing"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/metadata"
)
func TestConstructor(t *testing.T) {
tests := []struct {
config *Config
success bool
}{
{config: nil, success: true},
{config: &Config{Watchers: []string{metadata.WatcherID}}, success: true},
{config: &Config{Watchers: []string{"foobar"}}, success: false},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
_, err := New(tt.config)
if err != nil && tt.success {
t.Errorf("expected success, got error: %+v", err)
}
})
}
}
func TestInitWatcers(t *testing.T) {
tests := []struct {
watchers []Watcher
success bool
}{
{watchers: []Watcher{metadata.New()}, success: true},
{watchers: []Watcher{&testWatcher{}}, success: false},
}
for i, tt := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
err := initWatchers(tt.watchers)
if err != nil && tt.success {
t.Errorf("expected success, got error: %+v", err)
}
})
}
}
type testWatcher struct {
watcherID string
counter int
maxCount int
}
func (tprod *testWatcher) ID() string {
return tprod.watcherID
}
func (tprod *testWatcher) Events() []string {
return []string{tprod.watcherID + ",test-event"}
}
func (tprod *testWatcher) Run(ctx context.Context, evType string) (bool, interface{}, error) {
tprod.counter++
evData := tprod.counter
if tprod.counter >= tprod.maxCount {
return false, nil, nil
}
return true, &evData, nil
}
func TestRun(t *testing.T) {
watcherID := "test-watcher"
maxCount := 10
err := initWatchers([]Watcher{
&testWatcher{
watcherID: watcherID,
maxCount: maxCount,
},
})
if err != nil {
t.Fatalf("Failed to init/register watcher: %+v", err)
}
eventManager, err := New(&Config{Watchers: []string{watcherID}})
if err != nil {
t.Fatalf("Failed to init event manager: %+v", err)
}
counter := 0
eventManager.Subscribe("test-watcher,test-event", &counter, func(ctx context.Context, evType string, data interface{}, evData *EventData) bool {
dd := data.(*int)
*dd++
return true
})
eventManager.Run(context.Background())
if counter != maxCount {
t.Errorf("Failed to increment callback counter, expected: %d, got: %d", maxCount, counter)
}
}
func TestUnsubscribe(t *testing.T) {
watcherID := "test-watcher"
maxCount := 10
unsubscribeAt := 2
err := initWatchers([]Watcher{
&testWatcher{
watcherID: watcherID,
maxCount: maxCount,
},
})
if err != nil {
t.Fatalf("Failed to init/register watcher: %+v", err)
}
eventManager, err := New(&Config{Watchers: []string{watcherID}})
if err != nil {
t.Fatalf("Failed to init event manager: %+v", err)
}
counter := 0
eventManager.Subscribe("test-watcher,test-event", nil, func(ctx context.Context, evType string, data interface{}, evData *EventData) bool {
if counter == unsubscribeAt {
return false
}
counter++
return true
})
eventManager.Run(context.Background())
if counter != unsubscribeAt {
t.Errorf("Failed to unsubscribe callback, expected: %d, got: %d", unsubscribeAt, counter)
}
}
func TestCancelBeforeCallbacks(t *testing.T) {
watcherID := "test-watcher"
timeout := (1 * time.Second) / 100
err := initWatchers([]Watcher{
&testCancel{
watcherID: watcherID,
timeout: timeout,
},
})
if err != nil {
t.Fatalf("Failed to init/register watcher: %+v", err)
}
eventManager, err := New(&Config{Watchers: []string{watcherID}})
if err != nil {
t.Fatalf("Failed to init event manager: %+v", err)
}
eventManager.Subscribe("test-watcher,test-event", nil, func(ctx context.Context, evType string, data interface{}, evData *EventData) bool {
t.Errorf("Expected to have canceled before calling callback")
return true
})
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(timeout / 2)
cancel()
}()
eventManager.Run(ctx)
}
type testCancel struct {
watcherID string
timeout time.Duration
}
func (tc *testCancel) ID() string {
return tc.watcherID
}
func (tc *testCancel) Events() []string {
return []string{tc.watcherID + ",test-event"}
}
func (tc *testCancel) Run(ctx context.Context, evType string) (bool, interface{}, error) {
time.Sleep(tc.timeout)
return true, nil, nil
}
func TestCancelAfterCallbacks(t *testing.T) {
watcherID := "test-watcher"
timeout := (1 * time.Second) / 100
err := initWatchers([]Watcher{
&testCancel{
watcherID: watcherID,
timeout: timeout,
},
})
if err != nil {
t.Fatalf("Failed to init/register watcher: %+v", err)
}
eventManager, err := New(&Config{Watchers: []string{watcherID}})
if err != nil {
t.Fatalf("Failed to init event manager: %+v", err)
}
eventManager.Subscribe("test-watcher,test-event", nil, func(ctx context.Context, evType string, data interface{}, evData *EventData) bool {
return true
})
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(timeout * 10)
cancel()
}()
eventManager.Run(ctx)
}
type testCancelWatcher struct {
watcherID string
after int
}
func (tc *testCancelWatcher) ID() string {
return tc.watcherID
}
func (tc *testCancelWatcher) Events() []string {
return []string{tc.watcherID + ",test-event"}
}
func (tc *testCancelWatcher) Run(ctx context.Context, evType string) (bool, interface{}, error) {
time.Sleep(10 * time.Millisecond)
if tc.after == 0 {
return false, nil, nil
}
tc.after--
return true, nil, nil
}
func TestCancelCallbacksAndWatchers(t *testing.T) {
watcherID := "test-watcher"
tests := []struct {
cancelWatcherAfter int
cancelSubscriberAfter int
}{
{10, 20},
{20, 10},
{10, 10},
{0, 0},
{100, 200},
{200, 100},
{100, 100},
}
for i, curr := range tests {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
cancelSubscriberAfter := curr.cancelSubscriberAfter
err := initWatchers([]Watcher{
&testCancelWatcher{
watcherID: watcherID,
after: curr.cancelWatcherAfter,
},
})
if err != nil {
t.Fatalf("Failed to init/register watcher: %+v", err)
}
eventManager, err := New(&Config{Watchers: []string{watcherID}})
if err != nil {
t.Fatalf("Failed to init event manager: %+v", err)
}
eventManager.Subscribe("test-watcher,test-event", nil, func(ctx context.Context, evType string, data interface{}, evData *EventData) bool {
time.Sleep(1 * time.Millisecond)
if cancelSubscriberAfter == 0 {
return false
}
cancelSubscriberAfter--
return true
})
eventManager.Run(context.Background())
})
}
}
func TestMultipleEvents(t *testing.T) {
watcherID := "multiple-events"
firstEvent := "multiple-events,first-event"
secondEvent := "multiple-events,second-event"
err := initWatchers([]Watcher{
&testMultipleEvents{
watcherID: watcherID,
eventIDS: []string{firstEvent, secondEvent},
},
})
if err != nil {
t.Fatalf("Failed to init/register watcher: %+v", err)
}
eventManager, err := New(&Config{Watchers: []string{watcherID}})
if err != nil {
t.Fatalf("Failed to init event manager: %+v", err)
}
var hitFirstEvent bool
eventManager.Subscribe(firstEvent, nil, func(ctx context.Context, evType string, data interface{}, evData *EventData) bool {
hitFirstEvent = true
return false
})
var hitSecondEvent bool
eventManager.Subscribe(secondEvent, nil, func(ctx context.Context, evType string, data interface{}, evData *EventData) bool {
hitSecondEvent = true
return false
})
eventManager.Run(context.Background())
if !hitFirstEvent || !hitSecondEvent {
t.Errorf("Failed to call back events, first event hit? (%t), second event hit? (%t)", hitFirstEvent, hitSecondEvent)
}
}
type testMultipleEvents struct {
watcherID string
eventIDS []string
}
func (tt *testMultipleEvents) ID() string {
return tt.watcherID
}
func (tt *testMultipleEvents) Events() []string {
return tt.eventIDS
}
func (tt *testMultipleEvents) Run(ctx context.Context, evType string) (bool, interface{}, error) {
return false, nil, nil
}
guest-agent-20231004.02/google_guest_agent/events/metadata/ 0000775 0000000 0000000 00000000000 14507372607 0023352 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/events/metadata/metadata.go 0000664 0000000 0000000 00000004503 14507372607 0025463 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 metadata implement the metadata events watcher.
package metadata
import (
"context"
"net"
"net/url"
"time"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
const (
// WatcherID is the metadata watcher's ID.
WatcherID = "metadata-watcher"
// LongpollEvent is the metadata's longpoll event type ID.
LongpollEvent = "metadata-watcher,longpoll"
)
var (
// arbitrarily defined wait duration(keeps behavioral backward compatibility).
retryWaitDuration = 5 * time.Second
)
// Watcher is the metadata event watcher implementation.
type Watcher struct {
client metadata.MDSClientInterface
failedPrevious bool
}
// New allocates and initializes a new Watcher.
func New() *Watcher {
return &Watcher{
client: metadata.New(),
}
}
// ID returns the metadata event watcher id.
func (mp *Watcher) ID() string {
return WatcherID
}
// Events returns an slice with all implemented events.
func (mp *Watcher) Events() []string {
return []string{LongpollEvent}
}
// Run listens to metadata changes and report back the event.
func (mp *Watcher) Run(ctx context.Context, evType string) (bool, interface{}, error) {
descriptor, err := mp.client.Watch(ctx)
if err != nil {
// Only log error once to avoid transient errors and not to spam the log on network failures.
if !mp.failedPrevious {
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)
mp.failedPrevious = true
}
} else {
mp.failedPrevious = false
}
return true, descriptor, err
}
guest-agent-20231004.02/google_guest_agent/events/metadata/metadata_test.go 0000664 0000000 0000000 00000005273 14507372607 0026527 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 metadata
import (
"context"
"fmt"
"reflect"
"testing"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
)
var (
errUnknown = fmt.Errorf("simple error")
)
type mdsClient struct {
disableUnknownFailure bool
}
func (mds *mdsClient) Get(ctx context.Context) (*metadata.Descriptor, error) {
return nil, fmt.Errorf("Get() not yet implemented")
}
func (mds *mdsClient) GetKey(ctx context.Context, key string, headers map[string]string) (string, error) {
return "", fmt.Errorf("GetKey() not yet implemented")
}
func (mds *mdsClient) Watch(ctx context.Context) (*metadata.Descriptor, error) {
if !mds.disableUnknownFailure {
return nil, errUnknown
}
return nil, nil
}
func (mds *mdsClient) WriteGuestAttributes(ctx context.Context, key string, value string) error {
return fmt.Errorf("WriteGuestattributes() not yet implemented")
}
func TestWatcherAPI(t *testing.T) {
watcher := New()
expectedEvents := []string{LongpollEvent}
if !reflect.DeepEqual(watcher.Events(), expectedEvents) {
t.Fatalf("watcher.Events() returned: %+v, expected: %+v.", watcher.Events(), expectedEvents)
}
if watcher.ID() != WatcherID {
t.Errorf("watcher.ID() returned: %s, expected: %s.", watcher.ID(), WatcherID)
}
}
func TestWatcherSuccess(t *testing.T) {
watcher := New()
watcher.client = &mdsClient{disableUnknownFailure: true}
renew, evData, err := watcher.Run(context.Background(), LongpollEvent)
if err != nil {
t.Errorf("watcher.Run(%s) returned error: %+v, expected success.", LongpollEvent, err)
}
if !renew {
t.Errorf("watcher.Run(%s) returned renew: %t, expected: true.", LongpollEvent, renew)
}
switch evData.(type) {
case *metadata.Descriptor:
default:
t.Errorf("watcher.Run(%s) returned a non descriptor object.", LongpollEvent)
}
}
func TestWatcherUnknownFailure(t *testing.T) {
watcher := New()
watcher.client = &mdsClient{}
renew, _, err := watcher.Run(context.Background(), LongpollEvent)
if err == nil {
t.Errorf("watcher.Run(%s) returned no error, expected: %v.", LongpollEvent, errUnknown)
}
if !renew {
t.Errorf("watcher.Run(%s) returned renew: %t, expected: true.", LongpollEvent, renew)
}
}
guest-agent-20231004.02/google_guest_agent/events/sshtrustedca/ 0000775 0000000 0000000 00000000000 14507372607 0024306 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/events/sshtrustedca/sshtrustedca.go 0000664 0000000 0000000 00000004033 14507372607 0027351 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 sshtrustedca implement the sshd trusted ca cert pipe events watcher.
package sshtrustedca
import (
"os"
"sync"
)
const (
// WatcherID is the sshtrustedca watcher's ID.
WatcherID = "ssh-trusted-ca-pipe-watcher"
// ReadEvent is the sshtrustedca's read event type ID.
ReadEvent = "ssh-trusted-ca-pipe-watcher,read"
// DefaultPipePath defines the default ssh trusted ca pipe path.
DefaultPipePath = "/etc/ssh/oslogin_trustedca.pub"
)
// Watcher is the sshtrustedca event watcher implementation.
type Watcher struct {
// pipePath points to the named pipe it's writing to.
pipePath string
// waitingWrite is a flag to inform the Watcher that the Handler has or
// hasn't finished writing.
waitingWrite bool
// mutex protects waitingWrite on concurrent accesses.
mutex sync.Mutex
}
// PipeData wraps the pipe event data.
type PipeData struct {
// File is the writeonly pipe's file descriptor. The user/handler must
// make sure to close it after processing the event.
File *os.File
// Finished is a callback used by the event handler to inform the write to
// the pipe is finished.
Finished func()
}
// New allocates and initializes a new Watcher.
func New(pipePath string) *Watcher {
return &Watcher{
pipePath: pipePath,
}
}
// ID returns the sshtrustedca event watcher id.
func (mp *Watcher) ID() string {
return WatcherID
}
// Events returns an slice with all implemented events.
func (mp *Watcher) Events() []string {
return []string{ReadEvent}
}
guest-agent-20231004.02/google_guest_agent/events/sshtrustedca/sshtrustedca_linux.go 0000664 0000000 0000000 00000007343 14507372607 0030577 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 sshtrustedca
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"syscall"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
// Create a named pipe if it doesn't exist.
func createNamedPipe(ctx context.Context, pipePath string) error {
pipeDir := filepath.Dir(pipePath)
_, err := os.Stat(pipeDir)
if err != nil && os.IsNotExist(err) {
// The perm 0755 is compatible with distros /etc/ssh/ directory.
if err := os.MkdirAll(pipeDir, 0755); err != nil {
return err
}
}
if _, err := os.Stat(pipePath); err != nil {
if os.IsNotExist(err) {
if err := syscall.Mkfifo(pipePath, 0644); err != nil {
return fmt.Errorf("failed to create named pipe: %+v", err)
}
} else {
return fmt.Errorf("failed to stat file: " + pipePath)
}
}
restorecon, err := exec.LookPath("restorecon")
if err != nil {
logger.Infof("No restorecon available, not restoring SELinux context of: %s", pipePath)
return nil
}
return run.Quiet(ctx, restorecon, pipePath)
}
// finishedCb is used by the event handler to communicate the write to the
// pipe is finised, it's exposed via PipeData.Finished pointer.
func (mp *Watcher) finishedCb() {
mp.setWaitingWrite(false)
}
func (mp *Watcher) isWaitingWrite() bool {
mp.mutex.Lock()
defer mp.mutex.Unlock()
return mp.waitingWrite
}
func (mp *Watcher) setWaitingWrite(val bool) {
mp.mutex.Lock()
defer mp.mutex.Unlock()
mp.waitingWrite = val
}
// Run listens to ssh_trusted_ca's pipe open calls and report back the event.
func (mp *Watcher) Run(ctx context.Context, evType string) (bool, interface{}, error) {
var canceled bool
for mp.isWaitingWrite() {
time.Sleep(10 * time.Millisecond)
}
// Channel used to cancel the context cancelation go routine.
// Used when the Watcher is returning to the event manager.
cancelContext := make(chan bool)
defer close(cancelContext)
// Cancelation handling code.
go func() {
select {
case <-cancelContext:
break
case <-ctx.Done():
canceled = true
// Open the pipe as O_RDONLY to release the blocking open O_WRONLY.
pipeFile, err := os.OpenFile(mp.pipePath, os.O_RDONLY, 0644)
if err != nil {
logger.Errorf("Failed to open readonly pipe: %+v", err)
return
}
defer func() {
if err := pipeFile.Close(); err != nil {
logger.Errorf("Failed to close readonly pipe: %+v", err)
}
}()
}
}()
// If the configured named pipe doesn't exists we create it before emitting events
// from it.
if err := createNamedPipe(ctx, mp.pipePath); err != nil {
return true, nil, err
}
// Open the pipe as writeonly, it will block until a read is performed from the
// other end of the pipe.
pipeFile, err := os.OpenFile(mp.pipePath, os.O_WRONLY, 0644)
if err != nil {
return true, nil, err
}
// Have we got a ctx.Done()? if so lets just return from here and unregister
// the watcher.
if canceled {
if err := pipeFile.Close(); err != nil {
logger.Errorf("Failed to close readonly pipe: %+v", err)
}
return false, nil, nil
}
cancelContext <- true
mp.setWaitingWrite(true)
return true, &PipeData{File: pipeFile, Finished: mp.finishedCb}, nil
}
guest-agent-20231004.02/google_guest_agent/events/sshtrustedca/sshtrustedca_linux_test.go 0000664 0000000 0000000 00000006021 14507372607 0031626 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 sshtrustedca
import (
"context"
"fmt"
"io"
"os"
"path"
"testing"
"time"
)
func TestPipe(t *testing.T) {
// Putting a directory name between temp dir and the file name guarantees we test
// the directory creation.
pipePath := path.Join(t.TempDir(), "ssh", "oslogin_trustedca.pub")
watcher := New(pipePath)
testData := "test data transmited through the pipe."
if watcher.ID() != WatcherID {
t.Errorf("Wrong watcher id, expected %s, got %s", WatcherID, watcher.ID())
}
timer := time.NewTimer(1 * time.Second)
// This go routine simulates the reading end of the pipe, it will until the timer
// is triggered (giving enough time for the Watcher to setup the pipe), when the
// read operation happened the Watcher will unblock returning to the test and
// the test implementation will write to the writing end of the pipe.
go func() {
<-timer.C
readFile, err := os.OpenFile(pipePath, os.O_RDONLY, 0644)
if err != nil {
t.Errorf("Failed to open the read end of the pipe: %+v", err)
return
}
defer func() {
if err := readFile.Close(); err != nil {
t.Errorf("Failed to close pipe(read end) file: %+v", err)
}
}()
buff := make([]byte, 1024)
var output string
for {
n, err := readFile.Read(buff)
if err == io.EOF {
break
}
if err != nil {
t.Errorf("Failed to read pipe: %+v", err)
return
}
if n > 0 {
output = fmt.Sprintf("%s%s", output, buff[:n])
}
}
if output != testData {
t.Errorf("Wrong data read from the pipe, expected %s, got %s", testData, output)
}
}()
_, evData, err := watcher.Run(context.Background(), ReadEvent)
if err != nil {
t.Fatalf("Watcher failed: %+v", err)
}
pipeData := evData.(*PipeData)
defer func() {
if err := pipeData.File.Close(); err != nil {
t.Fatalf("Failed to close pipe(write end) file: %+v", err)
}
pipeData.Finished()
}()
pipeData.File.WriteString(testData)
}
func TestCancel(t *testing.T) {
pipePath := path.Join(t.TempDir(), "ssh", "oslogin_trustedca.pub")
watcher := New(pipePath)
sync := make(chan bool)
defer close(sync)
cancelTimer := time.NewTimer((1 * time.Second) / 2)
timeoutTimer := time.NewTimer(1 * time.Second)
ctx, ctxCancel := context.WithCancel(context.Background())
go func() {
<-cancelTimer.C
ctxCancel()
sync <- true
}()
go func() {
select {
case <-timeoutTimer.C:
t.Error("Watcher should have been canceled before timeout.")
case <-sync:
return
}
}()
watcher.Run(ctx, ReadEvent)
}
guest-agent-20231004.02/google_guest_agent/events/sshtrustedca/sshtrustedca_windows.go 0000664 0000000 0000000 00000001552 14507372607 0031126 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 sshtrustedca
import (
"context"
"fmt"
)
// Run is a no-op implementation for windows.
func (mp *Watcher) Run(ctx context.Context, evType string) (bool, interface{}, error) {
return false, nil, fmt.Errorf("SSH Trusted CA cert pipe Watcher is not yet implemented for windows.")
}
guest-agent-20231004.02/google_guest_agent/fakes/ 0000775 0000000 0000000 00000000000 14507372607 0021357 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/fakes/fake_mds.go 0000664 0000000 0000000 00000010107 14507372607 0023456 0 ustar 00root root 0000000 0000000 // Copyright 2023 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 fakes implements fake services for unit testing.
package fakes
import (
"context"
"fmt"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
)
type contextKey int
const (
// MDSOverride is the context key used by fake MDS for getting test conditions.
MDSOverride contextKey = iota
)
// MDSClient implements fake metadata server.
type MDSClient struct{}
// NewFakeMDSClient returns fake type MDSClient.
func NewFakeMDSClient() *MDSClient {
return &MDSClient{}
}
// GetKey implements fake GetKey MDS method.
func (s MDSClient) GetKey(ctx context.Context, key string, headers map[string]string) (string, error) {
valid := `
{
"encrypted_credentials": "q3u9avkCiXCgKAopiG3WFKmIfwidMq+ISLEIufPDBq0EdVRt+5XnEqz1dJyNuqdeRNmP24VlsXaZ77wQtF/6qcg4t0JhUqn18VkodIUvhz8zFdYGe9peu5EprcC/h8MvSrKXS6WmWRn1920/itPo4yPKl31mOGaOwRuPYqNLVUUu1iFZZ3VZTTDp5yh3AyvLoO41UoKi6siZM+xo+PB+qoHcARGctvNfsZv+jZYbAh6PRuJ2kI4aBBp2sUFWQhAZOoDYqLpcrtTe1d9LeQC/PN/PVz5FiLOwu87YsnOGgt7/K1ce2AxDGRJaINHarricVXaqx38h0u8zei7ynTsSZIemNo9SoR6dH7feRaSiH23htHryJQMx8TV32XHzuE0GdApTLkHIqc0eZGmoJ/PGYy6INaVC+kpk+7tlZ3ZwkKneXgroyy20Iig+wfKMcj8i7ncLP01PMep9d7uFaCuoshdxJbAEeqPCNr59D7zfRBDg+QBavLKv3aPSMqFOYF1tqj2mOB1EHsasZgtDslSwDN7EhkR2YbBi2HNSNFKzEnh5SsbXINSyAgaffoK+99YrLRXCQpdaqr9GIRug6HzMzQMsXhIxr4yErVbpPcv7GSC21vi4PWU62zhvWUZ8w4HXds3HjvpJk3ILrglM72xfkddEdr1Hd7KP1F3h6nG+9FFP4s6Z6j7uHPrL+ppd7Od4dDc05hA+Unifoyshb+IaCJGtzewQtofLhyZcoEZBzp1iMT5IwSCZm6eHSwCG9hS7S9eKJAcjLBwSxWZhwO4UXU3mJM0ZTZfxUxXtmR9Ombpm5xpIu5fa4rMi1DUCKK2vrYDR5hYJrEUsFLzyK+4EGuWz+FPgMXi6gXMZZYVQCjS3zcnfBsEL18EvlDHs2muuHWE/gEjGO0nFCUFuNwkOY2bW+BU8/eKwosYxYhQk+jwYJFEuSXqtm+wgCEyFvIbg42GDc+YrKPTxAzWiBH/RL/XrPR4InDZ6extmSYZbneLjT1YRAAfLR/MOiWuY2I38Q2VYBzMqZ6y1/1EgToNMW2viYlxEVmN1ys0msospzxCGwlR0DWkSzEDJmYT2SQcKFC9OrdMZ2o6BD4s315M8lv5v7ZsL7KuoYNZ4gMBN6MrxJYD6OwdLeytCmI71LdvgVw5gdDmoChu9dFDyzPKSoMYJnvTr5ktrYwxZIyWn8Sl3BjAaslZkAwL+c5oijCTCZ+oV9vzdD7tBnFx9y3fVVFtMC3nflyEjInEUPCupxh38O4TsYLLVl7tttL696kUKdlHL1SRAFCX1Wb5p4WNSBzQQtTGU1dsw904CncAj32sW32oGFWqb4Bom1OzoV/equ32Anef8J95mF+ahmf1BvTUMUq5Az2mSi2/dFBhuhy7rhGQyVWpwCEzpzVpVlysDr5aWr8CLbDOLzJv3MIDM3QQ=",
"key_import_blob": {
"duplicate": "ACAFYwCs8qzuSCCTvS1iCIHVTDuEXrP7WNNYPGl44ZPARLbhYVWaSkttYk1J2ChEEwG+u0fRxBVF95nEbe3xzN17+pppFFKelB9Jlf+PybtE0rRMyIJ0CB4HT9w=",
"encrypted_seed": "ACBnqcxLycU+VUxeB89a7DCa0BSqOciydCReXia87EDLjQAgEUyXgTSjqA4tOxRNARnW5fw4B2p6AJFLD1nZx+llJP8=",
"public_area": "AAgACwAAAEAAAAAQACCmhjk4ZFa6nbv58ya74lshnfNfGaCta6+hPIR5s+hZBw=="
}
}
`
invalid := `
{
"encrypted_credentials": "q3u9avkCLOwu87YsnOmNo9SoR6d/dFBhuhy7rhGQyVWpwCEzpzVpVlysDr5aWr8CLbDOLzJv3MIDM3QQ=",
"key_import_blob": {
"duplicate": "ACAFYwCs8qzuSCCTvS1iCIHVT9Jlf+PybtE0rRMyIJ0CB4HT9w=",
"encrypted_seed": "ACBnqcxLycU+VUxeB8D1nZx+llJP8=",
"public_area": "AAgACwAAAEAAAA+hPIR5s+hZBw=="
}
}
`
switch ctx.Value(MDSOverride) {
case "succeed":
return valid, nil
case "fail_mds_connect":
return "", fmt.Errorf("this is fake MDS error")
case "fail_unmarshal":
return invalid, nil
default:
return "", nil
}
}
// Get method implements fake Get on MDS.
func (s MDSClient) Get(context.Context) (*metadata.Descriptor, error) {
return nil, fmt.Errorf("not yet implemented")
}
// Watch method implements fake watcher on MDS.
func (s MDSClient) Watch(context.Context) (*metadata.Descriptor, error) {
return nil, fmt.Errorf("not yet implemented")
}
// WriteGuestAttributes method implements fake writer on MDS.
func (s MDSClient) WriteGuestAttributes(context.Context, string, string) error {
return fmt.Errorf("not yet implemented")
}
guest-agent-20231004.02/google_guest_agent/instance_setup.go 0000664 0000000 0000000 00000024457 14507372607 0023655 0 ustar 00root root 0000000 0000000 // Copyright 2019 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
// https://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"
"net"
"os"
"os/exec"
"runtime"
"sort"
"strings"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"github.com/go-ini/ini"
)
func getDefaultAdapter(fes []ipForwardEntry) (*ipForwardEntry, error) {
// Choose the first adapter index that has the default route setup.
// This is equivalent to how route.exe works when interface is not provided.
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")) {
return &fe, nil
}
}
return nil, fmt.Errorf("could not find default route")
}
func addMetadataRoute() error {
fes, err := getIPForwardEntries()
if err != nil {
return err
}
defaultRoute, err := getDefaultAdapter(fes)
if err != nil {
return err
}
forwardEntry := ipForwardEntry{
ipForwardDest: net.ParseIP("169.254.169.254"),
ipForwardMask: net.IPv4Mask(255, 255, 255, 255),
ipForwardNextHop: net.ParseIP("0.0.0.0"),
ipForwardMetric1: defaultRoute.ipForwardMetric1, // Must be <= the default route metric.
ipForwardIfIndex: defaultRoute.ipForwardIfIndex,
}
for _, fe := range fes {
if fe.ipForwardDest.Equal(forwardEntry.ipForwardDest) && fe.ipForwardIfIndex == forwardEntry.ipForwardIfIndex {
// No need to add entry, it's already setup.
return nil
}
}
logger.Infof("Adding route to metadata server on adapter with index %d", defaultRoute.ipForwardIfIndex)
return addIPForwardEntry(forwardEntry)
}
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
config := cfg.Get()
if runtime.GOOS == "windows" {
// Indefinitely retry to set up required MDS route.
for ; ; time.Sleep(1 * time.Second) {
if err := addMetadataRoute(); err != nil {
logger.Errorf("Could not set default route to metadata: %v", err)
} else {
break
}
}
} else {
// Linux instance setup.
defer run.Quiet(ctx, "systemd-notify", "--ready")
defer logger.Debugf("notify systemd")
if config.Snapshots.Enabled {
logger.Infof("Snapshot listener enabled")
snapshotServiceIP := config.Snapshots.SnapshotServiceIP
snapshotServicePort := config.Snapshots.SnapshotServicePort
timeoutInSeconds := config.Snapshots.TimeoutInSeconds
startSnapshotListener(ctx, snapshotServiceIP, snapshotServicePort, timeoutInSeconds)
}
scripts := []struct {
enabled bool
script string
}{
{config.InstanceSetup.OptimizeLocalSSD, "optimize_local_ssd"},
{config.InstanceSetup.SetMultiqueue, "set_multiqueue"},
}
// These scripts are run regardless of metadata/network access and config options.
for _, curr := range scripts {
if !curr.enabled {
continue
}
if err := run.Quiet(ctx, "google_"+curr.script); err != nil {
logger.Warningf("Failed to run %q script: %v", "google_"+curr.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.InstanceSetup.NetworkEnabled {
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, _ = mdsClient.Get(ctx)
}
// Disable overcommit accounting; e2 instances only.
parts := strings.Split(newMetadata.Instance.MachineType, "/")
if strings.HasPrefix(parts[len(parts)-1], "e2-") {
if err := run.Quiet(ctx, "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.Instance.InstanceIDDir
instanceID, err := os.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.Instance.InstanceID)
// Write instance ID to file for next time before moving on.
towrite := fmt.Sprintf("%s\n", newMetadata.Instance.ID.String())
if err := os.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.InstanceSetup.SetHostKeys {
if err := generateSSHKeys(ctx); err != nil {
logger.Warningf("Failed to generate SSH keys: %v", err)
}
}
if config.InstanceSetup.SetBotoConfig {
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 := os.WriteFile(instanceIDFile, []byte(towrite), 0644); err != nil {
logger.Warningf("Failed to write instance ID file: %v", err)
}
}
}
}
}
func generateSSHKeys(ctx context.Context) error {
config := cfg.Get()
hostKeyDir := config.InstanceSetup.HostKeyDir
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.InstanceSetup.HostKeyTypes
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 := run.Quiet(ctx, "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 := os.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 := mdsClient.WriteGuestAttributes(ctx, "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")
}
}
_, err = exec.LookPath("restorecon")
if err == nil {
if err := run.Quiet(ctx, "restorecon", "-FR", hostKeyDir); err != nil {
return fmt.Errorf("failed to restore SELinux context for: %s", 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 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-20231004.02/google_guest_agent/instance_setup_integ_test.go 0000664 0000000 0000000 00000012352 14507372607 0026071 0 ustar 00root root 0000000 0000000 // 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.
//go:build integration
// +build integration
package main
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
)
const (
botoCfg = "/etc/boto.cfg"
)
func getConfig(t *testing.T) (*cfg.Sections, string) {
t.Helper()
if err := cfg.Load(nil); err != nil {
t.Fatalf("Failed to load configuration: %+v", err)
}
config, err := cfg.Get()
if err != nil {
t.Fatalf("Failed to get config: %+v", err)
}
if config == nil {
t.Fatal("cfg.Get() returned a nil config")
}
tempDir := filepath.Join(t.TempDir(), "test_instance_setup")
err := os.Mkdir(tempDir, 0755)
if err != nil {
t.Fatalf("Failed to create working dir: %+v", err)
}
// Configure a non-standard instance ID dir for us to play with.
config.Instance.InstanceIDDir = tempDir
config.InstanceSetup.HostKeyDir = tempDir
return config, tempDir
}
// TestInstanceSetupSSHKeys validates SSH keys are generated on first boot and not changed afterward.
func TestInstanceSetupSSHKeys(t *testing.T) {
config, tempDir := getConfig(t)
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) {
config, tempDir := getConfig(t)
// Disable SSH host key generation.
config.InstanceSetup.SetHostKeys = 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) {
config, tempDir := getConfig(t)
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) {
config, _ := getConfig(t, 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-20231004.02/google_guest_agent/main.go 0000664 0000000 0000000 00000017515 14507372607 0021552 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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 (
"context"
"fmt"
"io"
"os"
"runtime"
"strings"
"sync"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/agentcrypto"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events"
mdsEvent "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/metadata"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/sshtrustedca"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/osinfo"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/scheduler"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/sshca"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/telemetry"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-agent/utils"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
// Certificates wrapps a list of certificate authorities.
type Certificates struct {
Certs []TrustedCert `json:"trustedCertificateAuthorities"`
}
// TrustedCert defines the object containing a public key.
type TrustedCert struct {
PublicKey string `json:"publicKey"`
}
var (
programName = "GCEGuestAgent"
version string
oldMetadata, newMetadata *metadata.Descriptor
osInfo osinfo.OSInfo
mdsClient *metadata.Client
)
const (
regKeyBase = `SOFTWARE\Google\ComputeEngine`
)
type manager interface {
Diff(ctx context.Context) (bool, error)
Disabled(ctx context.Context) (bool, error)
Set(ctx context.Context) error
Timeout(ctx context.Context) (bool, error)
}
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 closeFile(c io.Closer) {
err := c.Close()
if err != nil {
logger.Warningf("Error closing file: %v.", err)
}
}
func availableManagers() []manager {
managers := []manager{
&addressMgr{},
}
if runtime.GOOS == "windows" {
return append(managers,
newWsfcManager(),
&winAccountsMgr{},
&diagnosticsMgr{},
)
}
return append(managers,
&clockskewMgr{},
&osloginMgr{},
&accountsMgr{},
)
}
func runUpdate(ctx context.Context) {
var wg sync.WaitGroup
for _, mgr := range availableManagers() {
wg.Add(1)
go func(mgr manager) {
defer wg.Done()
disabled, err := mgr.Disabled(ctx)
if err != nil {
logger.Errorf("Failed to run manager's Disabled() call: %+v", err)
return
}
if disabled {
logger.Debugf("manager %#v disabled, skipping", mgr)
return
}
timeout, err := mgr.Timeout(ctx)
if err != nil {
logger.Errorf("[%#v] Failed to run manager Timeout() call: %+v", mgr, err)
return
}
diff, err := mgr.Diff(ctx)
if err != nil {
logger.Errorf("[%#v] Failed to run manager Diff() call: %+v", mgr, err)
return
}
if !timeout && !diff {
logger.Debugf("[%#v] Manager reports no diff", mgr)
return
}
logger.Debugf("running %#v manager", mgr)
if err := mgr.Set(ctx); err != nil {
logger.Errorf("[%#v] Failed to run manager Set() call: %s", mgr, err)
}
}(mgr)
}
wg.Wait()
}
func runAgent(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
}
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)
osInfo = osinfo.Get()
mdsClient = metadata.New()
agentInit(ctx)
// Previous request to metadata *may* not have worked becasue routes don't get added until agentInit.
var err error
if newMetadata == nil {
/// Error here doesn't matter, if we cant get metadata, we cant record telemetry.
newMetadata, err = mdsClient.Get(ctx)
if err != nil {
logger.Debugf("Error getting metdata: %v", err)
}
}
// Try to re-initialize logger now, we know after agentInit() is more likely to have metadata available.
// TODO: move all this metadata dependent code to its own metadata event handler.
if newMetadata != nil {
opts.ProjectName = newMetadata.Project.ProjectID
if err := logger.Init(ctx, opts); err != nil {
logger.Errorf("Error initializing logger: %v", err)
}
}
// knownJobs is list of default jobs that run on a pre-defined schedule.
knownJobs := []scheduler.Job{telemetry.New(mdsClient, programName, version)}
scheduler.ScheduleJobs(ctx, knownJobs, false)
// Schedules jobs that need to be started before notifying systemd Agent process has started.
if cfg.Get().Unstable.MDSMTLS {
scheduler.ScheduleJobs(ctx, []scheduler.Job{agentcrypto.New()}, true)
}
eventsConfig := &events.Config{
Watchers: []string{
mdsEvent.WatcherID,
sshtrustedca.WatcherID,
},
}
eventManager, err := events.New(eventsConfig)
if err != nil {
logger.Errorf("Error initializing event manager: %v", err)
return
}
sshca.Init(eventManager)
oldMetadata = &metadata.Descriptor{}
eventManager.Subscribe(mdsEvent.LongpollEvent, nil, func(ctx context.Context, evType string, data interface{}, evData *events.EventData) bool {
logger.Debugf("Handling metadata %q event.", evType)
// If metadata watcher failed there isn't much we can do, just ignore the event and
// allow the water to get it corrected.
if evData.Error != nil {
logger.Infof("Metadata event watcher failed, ignoring: %+v", evData.Error)
return true
}
if evData.Data == nil {
logger.Infof("Metadata event watcher didn't pass in the metadata, ignoring.")
return true
}
newMetadata = evData.Data.(*metadata.Descriptor)
runUpdate(ctx)
oldMetadata = newMetadata
return true
})
eventManager.Run(ctx)
logger.Infof("GCE Agent Stopped")
}
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()
if err := cfg.Load(nil); err != nil {
fmt.Fprintf(os.Stderr, "Failed to load configuration: %+v", err)
os.Exit(1)
}
var action string
if len(os.Args) < 2 {
action = "run"
} else {
action = os.Args[1]
}
if action == "noservice" {
runAgent(ctx)
os.Exit(0)
}
if err := register(ctx, "GCEAgent", "GCEAgent", "", runAgent, action); err != nil {
logger.Fatalf("error registering service: %s", err)
}
}
guest-agent-20231004.02/google_guest_agent/non_windows_accounts.go 0000664 0000000 0000000 00000033610 14507372607 0025063 0 ustar 00root root 0000000 0000000 // Copyright 2019 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
// https://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"
"context"
"fmt"
"os"
"os/exec"
"path"
"runtime"
"sort"
"strconv"
"strings"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"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
}
func removeExpiredKeys(keys []string) []string {
var validKeys []string
for _, key := range keys {
if err := utils.CheckExpiredKey(key); err == nil {
validKeys = append(validKeys, key)
}
}
return validKeys
}
type accountsMgr struct{}
func (a *accountsMgr) Diff(ctx context.Context) (bool, error) {
// If any keys have changed.
if !compareStringSlice(newMetadata.Instance.Attributes.SSHKeys, oldMetadata.Instance.Attributes.SSHKeys) {
return true, nil
}
if !compareStringSlice(newMetadata.Project.Attributes.SSHKeys, oldMetadata.Project.Attributes.SSHKeys) {
return true, nil
}
if newMetadata.Instance.Attributes.BlockProjectKeys != oldMetadata.Instance.Attributes.BlockProjectKeys {
return true, nil
}
// If any on-disk keys have expired.
for _, keys := range sshKeys {
if len(keys) != len(removeExpiredKeys(keys)) {
return true, nil
}
}
// If we've just disabled OS Login.
oldOslogin, _, _ := getOSLoginEnabled(oldMetadata)
newOslogin, _, _ := getOSLoginEnabled(newMetadata)
if oldOslogin && !newOslogin {
return true, nil
}
return false, nil
}
func (a *accountsMgr) Timeout(ctx context.Context) (bool, error) {
return false, nil
}
func (a *accountsMgr) Disabled(ctx context.Context) (bool, error) {
config := cfg.Get()
oslogin, _, _ := getOSLoginEnabled(newMetadata)
return false || runtime.GOOS == "windows" || oslogin || !config.Daemons.AccountsDaemon, nil
}
func (a *accountsMgr) Set(ctx context.Context) error {
config := cfg.Get()
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(ctx, config); 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(ctx, config, 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(ctx, 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(ctx, 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(ctx, config, 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(ctx, 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 {
err = utils.ValidateUserKey(user, keyVal)
}
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 := os.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) (string, []string) {
cmd = strings.Replace(cmd, "{user}", user, 1)
cmd = strings.Replace(cmd, "{group}", group, 1)
// We don't run the command here because we might need the exit codes.
tokens := strings.Fields(cmd)
return tokens[0], tokens[1:]
}
// createGoogleUser creates a Google managed user account if needed and adds it
// to the configured groups.
func createGoogleUser(ctx context.Context, config *cfg.Sections, user string) error {
var uid string
if config.Accounts.ReuseHomedir {
uid = getUID(fmt.Sprintf("/home/%s", user))
}
if err := createUser(ctx, user, uid); err != nil {
return err
}
groups := config.Accounts.Groups
for _, group := range strings.Split(groups, ",") {
addUserToGroup(ctx, user, group)
}
return addUserToGroup(ctx, 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(ctx context.Context, config *cfg.Sections, user string) error {
if config.Accounts.DeprovisionRemove {
userdel := config.Accounts.UserDelCmd
name, args := createUserGroupCmd(userdel, user, "")
return run.Quiet(ctx, name, args...)
}
if err := updateAuthorizedKeysFile(ctx, user, []string{}); err != nil {
return err
}
gpasswddel := config.Accounts.GPasswdRemoveCmd
name, args := createUserGroupCmd(gpasswddel, user, "google-sudoers")
return run.Quiet(ctx, name, args...)
}
// 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(ctx context.Context, config *cfg.Sections) error {
groupadd := config.Accounts.GroupAddCmd
name, args := createUserGroupCmd(groupadd, "", "google-sudoers")
ret := run.WithOutput(ctx, name, args...)
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(ctx context.Context, 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 := os.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)
}
_, err = exec.LookPath("restorecon")
if err == nil {
if err := run.Quiet(ctx, "restorecon", tempPath); err != nil {
return fmt.Errorf("error setting selinux context: %+v", err)
}
}
return os.Rename(tempPath, akpath)
}
guest-agent-20231004.02/google_guest_agent/non_windows_accounts_integ_test.go 0000664 0000000 0000000 00000006001 14507372607 0027302 0 ustar 00root root 0000000 0000000 // 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.
//go:build integration
// +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-20231004.02/google_guest_agent/osinfo/ 0000775 0000000 0000000 00000000000 14507372607 0021563 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/osinfo/osinfo.go 0000664 0000000 0000000 00000002436 14507372607 0023414 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 osinfo
import (
"fmt"
)
// OSInfo contains OS information about the system.
type OSInfo struct {
// OS name in short form.
OS string
// OS version ID.
VersionID string
// The name the OS uses to fully describe itself.
PrettyName string
// The kernel release.
KernelRelease string
// The kernel version.
KernelVersion string
// This is used by oslogin.go
Version Ver
}
// Ver describes the system version
type Ver struct {
Major, Minor, Patch, Length int
}
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
}
guest-agent-20231004.02/google_guest_agent/osinfo/osinfo_unix.go 0000664 0000000 0000000 00000007065 14507372607 0024462 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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.
//go:build unix
package osinfo
import (
"bytes"
"errors"
"fmt"
"os"
"strconv"
"strings"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"golang.org/x/sys/unix"
)
var (
osRelease = "/etc/os-release"
systemRelease = "/etc/system-release"
)
func parseOSRelease(osRelease string) (OSInfo, error) {
var ret OSInfo
for _, line := range strings.Split(osRelease, "\n") {
var id = line
if id = strings.TrimPrefix(line, "ID="); id != line {
id = strings.Trim(id, `"`)
ret.OS = parseID(id)
}
if id = strings.TrimPrefix(line, "VERSION_ID="); id != line {
id = strings.Trim(id, `"`)
ret.VersionID = id
version, err := parseVersion(id)
if err != nil {
return ret, fmt.Errorf("couldn't parse version id: %v", err)
}
ret.Version = version
}
if name := strings.TrimPrefix(line, "PRETTY_NAME="); name != line {
name = strings.Trim(name, `"`)
ret.PrettyName = name
}
}
return ret, nil
}
func parseSystemRelease(systemRelease string) (OSInfo, error) {
var ret OSInfo
var key = " release "
idx := strings.Index(systemRelease, key)
if idx == -1 {
return ret, errors.New("SystemRelease does not match expected format")
}
ret.OS = parseID(systemRelease[:idx])
versionFromRelease := strings.Split(systemRelease[idx+len(key):], " ")[0]
version, err := parseVersion(versionFromRelease)
if err != nil {
return ret, fmt.Errorf("couldn't parse version: %v", 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 parseRelease() (OSInfo, error) {
if releaseFile, err := os.ReadFile(osRelease); err == nil {
return parseOSRelease(string(releaseFile))
}
if releaseFile, err := os.ReadFile(systemRelease); err == nil {
return parseSystemRelease(string(releaseFile))
}
return OSInfo{}, errors.New("no known release file found")
}
// Get returns OSInfo on the running system.
func Get() OSInfo {
osInfo, err := parseRelease()
if err != nil {
// This is a non critical error, we can still return a partially populated OSInfo.
logger.Warningf("Error parsing release info: %v", err)
}
var uts unix.Utsname
if err := unix.Uname(&uts); err != nil {
logger.Warningf("unix.Uname error: %v", err)
return osInfo
}
osInfo.KernelVersion = string(bytes.TrimRight(uts.Version[:], "\x00"))
osInfo.KernelRelease = string(bytes.TrimRight(uts.Release[:], "\x00"))
return osInfo
}
guest-agent-20231004.02/google_guest_agent/osinfo/osinfo_unix_test.go 0000664 0000000 0000000 00000006202 14507372607 0025511 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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.
//go:build unix
package osinfo
import (
"reflect"
"testing"
)
func TestParseSystemRelease(t *testing.T) {
tests := []struct {
desc string
file string
want OSInfo
wantErr bool
}{
{"rhel 6.10", "Red Hat Enterprise Linux Server release 6.10 (Santiago)", OSInfo{OS: "rhel", Version: Ver{6, 10, 0, 2}}, false},
{"rhel 6.10.1", "Red Hat Enterprise Linux Server release 6.10.1", OSInfo{OS: "rhel", Version: Ver{6, 10, 1, 3}}, false},
{"centos 7.6.1810", "CentOS Linux release 7.6.1810 (Core)", OSInfo{OS: "centos", Version: Ver{7, 6, 1810, 3}}, false},
{"bad format", "CentOS Linux", OSInfo{}, true},
{"bad version", "CentOS Linux release Core", OSInfo{OS: "centos"}, true},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got, err := parseSystemRelease(tc.file)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("parseSystemRelease(%s) incorrect return: got %v, want %v", tc.file, got, tc.want)
}
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
t.Errorf("want error return: %T, got error: %v", tc.wantErr, err)
}
})
}
}
func TestParseOSRelease(t *testing.T) {
tests := []struct {
desc string
file string
want OSInfo
wantErr bool
}{
{"sles 12", "ID=\"sles\"\nPRETTY_NAME=\"SLES\"\nVERSION=\"12-SP4\"\nVERSION_ID=12", OSInfo{OS: "sles", PrettyName: "SLES", VersionID: "12", Version: Ver{12, 0, 0, 1}}, false},
{"sles 12.4", "ID=sles\nPRETTY_NAME=\"SLES\"\nVERSION=\"12-SP4\"\nVERSION_ID=\"12.4\"", OSInfo{OS: "sles", PrettyName: "SLES", VersionID: "12.4", Version: Ver{12, 4, 0, 2}}, false},
{"debian 9 (stretch)", "ID=debian\nPRETTY_NAME=\"Debian GNU/Linux\"\nVERSION=\"9 (stretch)\"\nVERSION_ID=\"9\"", OSInfo{OS: "debian", PrettyName: "Debian GNU/Linux", VersionID: "9", Version: Ver{9, 0, 0, 1}}, false},
{"debian 9", "ID=\"debian\"\nPRETTY_NAME=\"Debian GNU/Linux\"\nVERSION=9\nVERSION_ID=\"9\"", OSInfo{OS: "debian", VersionID: "9", PrettyName: "Debian GNU/Linux", Version: Ver{9, 0, 0, 1}}, false},
{"error version parsing", "ID=\"debian\"\nPRETTY_NAME=\"Debian GNU/Linux\"\nVERSION=9\nVERSION_ID=\"something\"", OSInfo{OS: "debian", PrettyName: "Debian GNU/Linux", VersionID: "something"}, true},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
got, err := parseOSRelease(tc.file)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("parseOSRelease(%s) incorrect return: got %+v, want %+v", tc.file, got, tc.want)
}
if (err != nil && !tc.wantErr) || (err == nil && tc.wantErr) {
t.Errorf("want error return: %T, got error: %v", tc.wantErr, err)
}
})
}
}
guest-agent-20231004.02/google_guest_agent/osinfo/osinfo_windows.go 0000664 0000000 0000000 00000013057 14507372607 0025167 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 osinfo
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"syscall"
"unsafe"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
var (
versionDLL = windows.NewLazySystemDLL("version.dll")
// https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfosizew
procGetFileVersionInfoSizeW = versionDLL.NewProc("GetFileVersionInfoSizeW")
// https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfow
procGetFileVersionInfoW = versionDLL.NewProc("GetFileVersionInfoW")
// https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-verqueryvaluew
procVerQueryValueW = versionDLL.NewProc("VerQueryValueW")
)
// getTranslation returns the anguage and code page identifier from the provided
// version-information block.
func getTranslation(block []byte) (string, error) {
var start uint
var length uint
blockStart := uintptr(unsafe.Pointer(&block[0]))
if ret, _, _ := procVerQueryValueW.Call(
blockStart,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(`\VarFileInfo\Translation`))),
uintptr(unsafe.Pointer(&start)),
uintptr(unsafe.Pointer(&length))); ret == 0 {
return "", errors.New("zero return code from VerQueryValueW indicates failure")
}
begin := int(start) - int(blockStart)
// For translation data length is bytes.
trans := block[begin : begin+int(length)]
// Each 'translation' is 4 bytes long (2 16-bit sections), we just want the
// first one for simplicity.
t := make([]byte, 4)
// 16-bit language ID little endian
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd318693(v=vs.85).aspx
t[0], t[1] = trans[1], trans[0]
// 16-bit code page ID little endian
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx
t[2], t[3] = trans[3], trans[2]
return fmt.Sprintf("%x", t), nil
}
// getStringFileInfo returns the string value file info name specific to the language and code page indicated.
func getStringFileInfo(block []byte, langCodePage, name string) (string, error) {
var start uint
var length uint
blockStart := uintptr(unsafe.Pointer(&block[0]))
if ret, _, _ := procVerQueryValueW.Call(
blockStart,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(fmt.Sprintf(`\StringFileInfo\%s\%s`, langCodePage, name)))),
uintptr(unsafe.Pointer(&start)),
uintptr(unsafe.Pointer(&length))); ret == 0 {
return "", errors.New("zero return code from VerQueryValueW indicates failure")
}
begin := int(start) - int(blockStart)
// For version information length is characters (UTF16).
result := block[begin : begin+int(2*length)]
// Result is UTF16LE.
u16s := make([]uint16, length)
for i := range u16s {
u16s[i] = uint16(result[i*2+1])<<8 | uint16(result[i*2])
}
return syscall.UTF16ToString(u16s), nil
}
func getVersion(block []byte, langCodePage string) (string, string, error) {
ver, err := getStringFileInfo(block, langCodePage, "FileVersion")
if err != nil {
return "", "", err
}
rel, err := getStringFileInfo(block, langCodePage, "ProductVersion")
return ver, rel, err
}
func getKernelInfo() (string, string, error) {
root := os.Getenv("SystemRoot")
if root == "" {
root = `C:\Windows`
}
path := filepath.Join(root, "System32", "ntoskrnl.exe")
if _, err := os.Stat(path); err != nil {
return "", "", err
}
pPtr := unsafe.Pointer(syscall.StringToUTF16Ptr(path))
size, _, _ := procGetFileVersionInfoSizeW.Call(
uintptr(pPtr))
if size <= 0 {
return "", "", errors.New("GetFileVersionInfoSize call failed, data size can not be 0")
}
info := make([]byte, size)
if ret, _, _ := procGetFileVersionInfoW.Call(
uintptr(pPtr),
0,
uintptr(len(info)),
uintptr(unsafe.Pointer(&info[0]))); ret == 0 {
return "", "", errors.New("zero return code from GetFileVersionInfoW indicates failure")
}
// This should be something like 040904b0 for US English UTF16LE.
langCodePage, err := getTranslation(info)
if err != nil {
return "", "", fmt.Errorf("getTranslation() error: %v", err)
}
return getVersion(info, langCodePage)
}
// Get returns OSInfo on the running system.
func Get() OSInfo {
var osInfo OSInfo
osInfo.OS = "windows"
kVersion, kRelease, err := getKernelInfo()
if err != nil {
logger.Warningf("getKernelInfo() error: %v", err)
return osInfo
}
osInfo.KernelVersion = kVersion
osInfo.KernelRelease = kRelease
// SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion is always 6.3 now, so we get os version from kernel release
vs := strings.Split(kRelease, ".")
if len(vs) == 4 {
osInfo.VersionID = strings.Join(vs[:3], ".")
}
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err != nil {
logger.Warningf("registry.OpenKey error: %v", err)
return osInfo
}
defer k.Close()
productName, _, err := k.GetStringValue("ProductName")
if err != nil {
logger.Warningf("GetStringValue('ProductName') error: %v", err)
return osInfo
}
osInfo.PrettyName = productName
return osInfo
}
guest-agent-20231004.02/google_guest_agent/oslogin.go 0000664 0000000 0000000 00000031356 14507372607 0022277 0 ustar 00root root 0000000 0000000 // Copyright 2019 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
// https://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"
"os/exec"
"runtime"
"strings"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/sshtrustedca"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"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.Descriptor) (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(ctx context.Context) (bool, error) {
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), nil
}
func (o *osloginMgr) Timeout(ctx context.Context) (bool, error) {
return false, nil
}
func (o *osloginMgr) Disabled(ctx context.Context) (bool, error) {
return runtime.GOOS == "windows", nil
}
func (o *osloginMgr) Set(ctx context.Context) 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(ctx)
}
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(ctx, 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(ctx, svc); err != nil {
logger.Errorf("Error reloading service: %v.", err)
}
}
now := fmt.Sprintf("%d", time.Now().Unix())
mdsClient.WriteGuestAttributes(ctx, "guest-agent/sshable", now)
if enable {
logger.Debugf("Create OS Login dirs, if needed")
if err := createOSLoginDirs(ctx); 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 := run.Quiet(ctx, "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"
// Certificate based authentication.
authorizedPrincipalsCommand := "AuthorizedPrincipalsCommand /usr/bin/google_authorized_principals %u %k"
authorizedPrincipalsUser := "AuthorizedPrincipalsCommandUser root"
trustedUserCAKeys := "TrustedUserCAKeys " + sshtrustedca.DefaultPipePath
twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive"
if (osInfo.OS == "rhel" || osInfo.OS == "centos") && osInfo.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}
if cfg.Get().OSLogin.CertAuthentication {
osLoginBlock = append(osLoginBlock, trustedUserCAKeys, authorizedPrincipalsCommand, authorizedPrincipalsUser)
}
osLoginBlock = append(osLoginBlock, 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 := os.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 := os.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)
}
func updatePAMsshdPamless(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"
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"
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, sessionHomeDir, googleBlockEnd}
filtered = append(topOfFile, filtered...)
filtered = append(filtered, bottomOfFile...)
}
return strings.Join(filtered, "\n")
}
func writePAMConfig(enable, twofactor bool) error {
pamsshd, err := os.ReadFile("/etc/pam.d/sshd")
if err != nil {
return err
}
proposed := updatePAMsshdPamless(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 := os.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(ctx context.Context) 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 {
run.Quiet(ctx, 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(ctx context.Context, servicename string) error {
if !systemctlUnitExists(ctx, servicename) {
return nil
}
return run.Quiet(ctx, "systemctl", "try-restart", servicename+".service")
}
// systemctlReloadOrRestart tries to reload a running systemd service if
// supported, restart otherwise. Stopped services will be started.
func systemctlReloadOrRestart(ctx context.Context, servicename string) error {
if !systemctlUnitExists(ctx, servicename) {
return nil
}
return run.Quiet(ctx, "systemctl", "reload-or-restart", servicename+".service")
}
// systemctlStart tries to start a stopped systemd service. Started services
// will be ignored.
func systemctlStart(ctx context.Context, servicename string) error {
if !systemctlUnitExists(ctx, servicename) {
return nil
}
return run.Quiet(ctx, "systemctl", "start", servicename+".service")
}
func systemctlUnitExists(ctx context.Context, servicename string) bool {
res := run.WithOutput(ctx, "systemctl", "list-units", "--all", servicename+".service")
return !strings.Contains(res.StdOut, "0 loaded units listed")
}
guest-agent-20231004.02/google_guest_agent/oslogin_test.go 0000664 0000000 0000000 00000033540 14507372607 0023333 0 ustar 00root root 0000000 0000000 // Copyright 2019 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
// https://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"
"fmt"
"strings"
"testing"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/sshtrustedca"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
)
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"
authorizedPrincipalsCommand := "AuthorizedPrincipalsCommand /usr/bin/google_authorized_principals %u %k"
authorizedPrincipalsUser := "AuthorizedPrincipalsCommandUser root"
trustedUserCAKeys := "TrustedUserCAKeys " + sshtrustedca.DefaultPipePath
twoFactorAuthMethods := "AuthenticationMethods publickey,keyboard-interactive"
matchblock1 := `Match User sa_*`
matchblock2 := ` AuthenticationMethods publickey`
var tests = []struct {
contents, want []string
enable, twofactor, skey, cert bool
}{
{
// Full block is created, any others removed.
contents: []string{
"line1",
googleBlockStart,
"line2",
googleBlockEnd,
},
want: []string{
googleBlockStart,
trustedUserCAKeys,
authorizedPrincipalsCommand,
authorizedPrincipalsUser,
authorizedKeysCommand,
authorizedKeysUser,
twoFactorAuthMethods,
challengeResponseEnable,
googleBlockEnd,
"line1",
googleBlockStart,
matchblock1,
matchblock2,
googleBlockEnd,
},
enable: true,
twofactor: true,
skey: false,
cert: true,
},
{
// 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,
cert: false,
},
{
// Full block is created, google comments removed.
contents: []string{
"line1",
googleComment,
"line2",
"line3",
},
want: []string{
googleBlockStart,
trustedUserCAKeys,
authorizedPrincipalsCommand,
authorizedPrincipalsUser,
authorizedKeysCommand,
authorizedKeysUser,
twoFactorAuthMethods,
challengeResponseEnable,
googleBlockEnd,
"line1",
"line3",
googleBlockStart,
matchblock1,
matchblock2,
googleBlockEnd,
},
enable: true,
twofactor: true,
skey: false,
cert: true,
},
{
// 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,
cert: false,
},
{
// Block is created without two-factor options.
contents: []string{
"line1",
"line2",
},
want: []string{
googleBlockStart,
trustedUserCAKeys,
authorizedPrincipalsCommand,
authorizedPrincipalsUser,
authorizedKeysCommand,
authorizedKeysUser,
googleBlockEnd,
"line1",
"line2",
},
enable: true,
twofactor: false,
skey: false,
cert: true,
},
{
// 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,
cert: false,
},
{
// Existing block is removed.
contents: []string{
"line1",
"line2",
googleBlockStart,
"line3",
googleBlockEnd,
},
want: []string{
"line1",
"line2",
},
enable: false,
twofactor: true,
skey: false,
cert: true,
},
{
// Existing block is removed.
contents: []string{
"line1",
"line2",
googleBlockStart,
"line3",
googleBlockEnd,
},
want: []string{
"line1",
"line2",
},
enable: false,
twofactor: true,
skey: false,
cert: false,
},
{
// Skey binary is chosen instead.
contents: []string{
"line1",
"line2",
googleBlockStart,
"line3",
googleBlockEnd,
},
want: []string{
googleBlockStart,
trustedUserCAKeys,
authorizedPrincipalsCommand,
authorizedPrincipalsUser,
authorizedKeysCommandSk,
authorizedKeysUser,
googleBlockEnd,
"line1",
"line2",
},
enable: true,
twofactor: false,
skey: true,
cert: true,
},
{
// 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,
cert: false,
},
}
if err := cfg.Load(nil); err != nil {
t.Fatalf("Failed to initialize configuration manager: %+v", err)
}
config := cfg.Get()
defaultCertAuthConfig := config.OSLogin.CertAuthentication
for idx, tt := range tests {
contents := strings.Join(tt.contents, "\n")
want := strings.Join(tt.want, "\n")
config.OSLogin.CertAuthentication = tt.cert
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)
}
}
config.OSLogin.CertAuthentication = defaultCertAuthConfig
}
func TestUpdatePAMsshdPamless(t *testing.T) {
authOSLogin := "auth [success=done perm_denied=die default=ignore] pam_oslogin_login.so"
authGroup := "auth [default=ignore] pam_group.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,
sessionHomeDir,
googleBlockEnd,
},
enable: true,
twofactor: true,
},
{
contents: []string{
"line1",
"line2",
},
want: []string{
googleBlockStart,
authGroup,
googleBlockEnd,
"line1",
"line2",
googleBlockStart,
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 {
t.Run(fmt.Sprintf("test-%d", idx), func(t *testing.T) {
contents := strings.Join(tt.contents, "\n")
want := strings.Join(tt.want, "\n")
if res := updatePAMsshdPamless(contents, tt.enable, tt.twofactor); res != want {
t.Errorf("want:\n%v\ngot:\n%v\n", 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.Descriptor
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-20231004.02/google_guest_agent/run/ 0000775 0000000 0000000 00000000000 14507372607 0021072 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/run/run.go 0000664 0000000 0000000 00000006216 14507372607 0022232 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 run is a package with utilities for running command and handling results.
package run
import (
"bytes"
"context"
"errors"
"os/exec"
"strings"
"time"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
// Result wraps a command execution result.
type Result struct {
// Exit code. Set to -1 if we failed to run the command.
ExitCode int
// Stderr or err.Error if we failed to run the command.
StdErr string
// Stdout or "" if we failed to run the command.
StdOut string
// Combined is the process' stdout and stderr combined.
Combined string
}
// Error return an error containing the stderr content.
func (e Result) Error() string {
return strings.TrimSuffix(e.StdErr, "\n")
}
// Quiet runs a command and doesn't return a result, but an error in case of failure.
func Quiet(ctx context.Context, name string, args ...string) error {
res := execCommand(exec.CommandContext(ctx, name, args...))
if res.ExitCode != 0 {
return res
}
return nil
}
// WithOutput runs a command and returns the result.
func WithOutput(ctx context.Context, name string, args ...string) *Result {
return execCommand(exec.CommandContext(ctx, name, args...))
}
// WithOutputTimeout runs a command with a defined timeout and returns its result.
func WithOutputTimeout(ctx context.Context, timeout time.Duration, name string, args ...string) *Result {
child, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
res := execCommand(exec.CommandContext(child, name, args...))
if child.Err() != nil && errors.Is(child.Err(), context.DeadlineExceeded) {
res.ExitCode = 124 // By convention
}
return res
}
// WithCombinedOutput returns a result with stderr and stdout combined in the Combined
// member of Result.
func WithCombinedOutput(ctx context.Context, name string, args ...string) *Result {
cmd := exec.CommandContext(ctx, name, args...)
output, err := cmd.CombinedOutput()
if err != nil {
exitCode := -1
if ee, ok := err.(*exec.ExitError); ok {
exitCode = ee.ExitCode()
}
return &Result{
ExitCode: exitCode,
StdErr: err.Error(),
}
}
return &Result{
Combined: string(output),
}
}
func execCommand(cmd *exec.Cmd) *Result {
var stdout, stderr bytes.Buffer
logger.Debugf("exec: %v", cmd)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
return &Result{
ExitCode: ee.ExitCode(),
StdOut: stdout.String(),
StdErr: stderr.String(),
}
}
return &Result{
ExitCode: -1,
StdErr: err.Error(),
}
}
return &Result{
ExitCode: 0,
StdOut: stdout.String(),
}
}
guest-agent-20231004.02/google_guest_agent/run/run_test.go 0000664 0000000 0000000 00000013400 14507372607 0023262 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 run
import (
"context"
"fmt"
"os"
"path"
"strings"
"testing"
"time"
)
// builds a set of data to be used in the tests
func buildDataContent(t *testing.T) string {
t.Helper()
rootDir := path.Join(t.TempDir(), fmt.Sprintf("%d", time.Now().UnixNano()))
if err := os.MkdirAll(rootDir, 0744); err != nil {
t.Fatalf("Failed to make test dir: %+v", err)
}
if err := os.WriteFile(path.Join(rootDir, "data"), []byte("random data"), 0644); err != nil {
t.Fatalf("Failed to write test data: %+v", err)
}
return rootDir
}
func TestQuietSuccess(t *testing.T) {
testDir := buildDataContent(t)
tests := []string{
"grep -R data " + testDir,
"echo 'foobar' >> " + path.Join(testDir, "foobar"),
"rm -Rf " + path.Join(testDir, "foobar"),
"echo",
}
for _, curr := range tests {
t.Run(curr, func(t *testing.T) {
tokens := strings.Split(curr, " ")
if err := Quiet(context.Background(), tokens[0], tokens[1:]...); err != nil {
t.Errorf("run.Quiet(%s) failed with error: %+v, expected success.", curr, err)
}
})
}
}
func TestQuietFail(t *testing.T) {
testDir := buildDataContent(t)
tests := []string{
"grep -R datax " + testDir,
"rm -R /root/data",
}
for _, curr := range tests {
t.Run(curr, func(t *testing.T) {
tokens := strings.Split(curr, " ")
if err := Quiet(context.Background(), tokens[0], tokens[1:]...); err == nil {
t.Errorf("run.Quiet(%s) command succeed, expected failure.", curr)
}
})
}
}
func TestOutputSuccess(t *testing.T) {
testDir := buildDataContent(t)
tests := []struct {
cmd string
output string
}{
{"grep -R data " + testDir, path.Join(testDir, "data") + ":random data\n"},
{"echo foobar", "foobar\n"},
{"echo -n foobar", "foobar"},
{"cat " + path.Join(testDir, "data"), "random data"},
}
for _, curr := range tests {
t.Run(curr.cmd, func(t *testing.T) {
tokens := strings.Split(curr.cmd, " ")
res := WithOutput(context.Background(), tokens[0], tokens[1:]...)
if res.ExitCode != 0 {
t.Errorf("run.WithOutput(%s) failed with exitCode: %b, expected success.", curr, res.ExitCode)
}
if res.StdOut != curr.output {
t.Errorf("run.WithOutput(%s) failed with stdout: %s, expected empty stdout.", curr.cmd, res.StdOut)
}
})
}
}
func TestOutputFail(t *testing.T) {
testDir := buildDataContent(t)
tests := []string{
"grep -R foobar " + testDir,
"cat /root/foobar",
}
for _, curr := range tests {
t.Run(curr, func(t *testing.T) {
tokens := strings.Split(curr, " ")
res := WithOutput(context.Background(), tokens[0], tokens[1:]...)
if res.ExitCode == 0 {
t.Errorf("run.WithOutput(%s) command succeeded, expected failure.", curr)
}
})
}
}
func TestCombinedOutputSuccess(t *testing.T) {
testDir := buildDataContent(t)
tests := []struct {
cmd string
output string
}{
{"grep -R data " + testDir, path.Join(testDir, "data") + ":random data\n"},
{"echo foobar", "foobar\n"},
{"echo -n foobar", "foobar"},
{"cat " + path.Join(testDir, "data"), "random data"},
}
for _, curr := range tests {
t.Run(curr.cmd, func(t *testing.T) {
tokens := strings.Split(curr.cmd, " ")
res := WithCombinedOutput(context.Background(), tokens[0], tokens[1:]...)
if res.ExitCode != 0 {
t.Errorf("run.WithCombinedOutput(%s) failed with exitCode: %b, expected success.", curr, res.ExitCode)
}
if res.Combined != curr.output {
t.Errorf("run.WithCombinedOutput(%s) failed with stdout: %s, expected empty stdout.", curr.cmd, res.StdOut)
}
})
}
}
func TestCombinedOutputFail(t *testing.T) {
testDir := buildDataContent(t)
tests := []string{
"grep -R foobar " + testDir,
"cat /root/foobar",
}
for _, curr := range tests {
t.Run(curr, func(t *testing.T) {
tokens := strings.Split(curr, " ")
res := WithCombinedOutput(context.Background(), tokens[0], tokens[1:]...)
if res.ExitCode == 0 {
t.Errorf("run.WithCombinedoutput(%s) command succeeded, expected failure.", curr)
}
})
}
}
func TestOutputTimeoutSuccess(t *testing.T) {
testDir := buildDataContent(t)
tests := []struct {
cmd string
output string
}{
{"grep -R data " + testDir, path.Join(testDir, "data") + ":random data\n"},
{"echo foobar", "foobar\n"},
{"echo -n foobar", "foobar"},
{"cat " + path.Join(testDir, "data"), "random data"},
}
for _, curr := range tests {
t.Run(curr.cmd, func(t *testing.T) {
tokens := strings.Split(curr.cmd, " ")
res := WithOutputTimeout(context.Background(), 1*time.Second, tokens[0], tokens[1:]...)
if res.ExitCode != 0 {
t.Errorf("run.WithOutputTimeout(%s) command failed with exitcode: %d, expected 0.", curr, res.ExitCode)
}
if res.StdOut != curr.output {
t.Errorf("run.WithOutputTimeout(%s) command failed with stdout: %s, expected empty stdout.", curr.cmd, res.StdOut)
}
})
}
}
func TestOutputTimeoutFail(t *testing.T) {
testDir := buildDataContent(t)
tests := []string{
"grep -R foobar " + testDir,
"cat /root/foobar",
}
for _, curr := range tests {
t.Run(curr, func(t *testing.T) {
tokens := strings.Split(curr, " ")
res := WithOutputTimeout(context.Background(), 1*time.Second, tokens[0], tokens[1:]...)
if res.ExitCode == 0 {
t.Errorf("run.WithOutputTimeout(%s) command succeeded, expected failure.", curr)
}
})
}
}
guest-agent-20231004.02/google_guest_agent/scheduler/ 0000775 0000000 0000000 00000000000 14507372607 0022244 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/scheduler/logger.go 0000664 0000000 0000000 00000001643 14507372607 0024056 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 scheduler
import (
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
type cronLogger struct{}
func (cl *cronLogger) Info(msg string, keysAndValues ...any) {
logger.Infof("%s: %+v", msg, keysAndValues)
}
func (cl *cronLogger) Error(err error, msg string, keysAndValues ...any) {
logger.Infof("%s: %+v", msg, keysAndValues)
}
guest-agent-20231004.02/google_guest_agent/scheduler/scheduler.go 0000664 0000000 0000000 00000012051 14507372607 0024550 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 scheduler maintains scheduler utility for scheduling arbitrary jobs.
package scheduler
import (
"context"
"fmt"
"sync"
"time"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"github.com/robfig/cron/v3"
)
// Job defines the interface between the schedule manager and the actual job.
type Job interface {
// ID returns the job id.
ID() string
// Interval returns the interval at which job should be rescheduled and
// a bool determining if job should be scheduled starting now.
// If false, first run will be at time now+interval.
Interval() (time.Duration, bool)
// ShouldEnable specifies if the job should be enabled for scheduling.
ShouldEnable(context.Context) bool
// Run triggers the job for single execution. It returns error if any
// and a bool stating if scheduler should continue or stop scheduling.
Run(context.Context) (bool, error)
}
// Scheduler implements job schedule manager and offers a way to schedule/unschedule new jobs.
type Scheduler struct {
cron *cron.Cron
jobs map[string]cron.EntryID
mu sync.RWMutex
}
var scheduler *Scheduler
func init() {
taskIDs := make(map[string]cron.EntryID)
cron := cron.New(cron.WithLogger(&cronLogger{}))
scheduler = &Scheduler{
cron: cron,
jobs: taskIDs,
mu: sync.RWMutex{},
}
}
// Get starts and returns scheduler instance.
func Get() *Scheduler {
scheduler.start()
return scheduler
}
// getFunc generates a wrapper function for cron scheduler.
func (s *Scheduler) getFunc(ctx context.Context, job Job) func() {
f := func() {
schedule, err := job.Run(ctx)
if !schedule {
s.UnscheduleJob(job.ID())
}
if err != nil {
logger.Errorf("Failed to execute job %s: %v", job.ID(), err)
}
}
return f
}
// ScheduleJob adds a job to schedule at defined interval.
func (s *Scheduler) ScheduleJob(ctx context.Context, job Job, synchronous bool) error {
if !job.ShouldEnable(ctx) {
return fmt.Errorf("ShouldEnable() returned false, cannot schedule job %s", job.ID())
}
logger.Infof("Scheduling job: %s", job.ID())
interval, startNow := job.Interval()
if err := s.jobInit(job.ID(), interval, s.getFunc(ctx, job), startNow, synchronous); err != nil {
return err
}
return nil
}
func (s *Scheduler) setEntryID(jobID string, entryID cron.EntryID) {
s.mu.Lock()
defer s.mu.Unlock()
s.jobs[jobID] = entryID
}
// jobInit adds job to the schedule to run at specified interval.
// Setting startImmediately to true executes first run immediately, otherwise
// first run will be after interval (at now+interval).
// If startImmediately and synchronous both are true, init method will block
// until job is completed.
func (s *Scheduler) jobInit(jobID string, interval time.Duration, job func(), startImmediately, synchronous bool) error {
logger.Infof("Scheduling job %q to run at %f hr interval", jobID, interval.Hours())
_, found := s.jobs[jobID]
// If found, job is already running, return.
if found {
logger.Infof("Skipping, job %q is already scheduled", jobID)
return nil
}
entry, err := s.cron.AddFunc(fmt.Sprintf("@every %ds", int(interval.Seconds())), job)
if err != nil {
return fmt.Errorf("unable to schedule %q: %w", jobID, err)
}
s.setEntryID(jobID, entry)
if startImmediately {
if synchronous {
job()
} else {
// Start job in a go routine to not block the caller.
go job()
}
}
return nil
}
// UnscheduleJob removes the job from schedule.
func (s *Scheduler) UnscheduleJob(jobID string) {
s.mu.RLock()
defer s.mu.RUnlock()
logger.Infof("Unscheduling job %q", jobID)
entry, found := s.jobs[jobID]
if found {
s.cron.Remove(entry)
delete(s.jobs, jobID)
}
}
// start begins executing each job at defined interval.
func (s *Scheduler) start() {
logger.Infof("Starting the scheduler to run jobs")
s.cron.Start()
}
// Stop stops executing new jobs.
func (s *Scheduler) Stop() {
logger.Infof("Stopping the scheduler")
s.cron.Stop()
}
// ScheduleJobs schedules required jobs and waits for it to finish if synchronous is true.
func ScheduleJobs(ctx context.Context, jobs []Job, synchronous bool) {
wg := sync.WaitGroup{}
sched := Get()
var ids []string
for _, job := range jobs {
wg.Add(1)
ids = append(ids, job.ID())
go func(job Job) {
defer wg.Done()
if err := sched.ScheduleJob(ctx, job, synchronous); err != nil {
logger.Errorf("Failed to schedule job %s with error: %v", job.ID(), err)
} else {
logger.Infof("Successfully scheduled job %s", job.ID())
}
}(job)
}
if synchronous {
logger.Debugf("Waiting for %v to finish...", ids)
wg.Wait()
}
}
guest-agent-20231004.02/google_guest_agent/scheduler/scheduler_test.go 0000664 0000000 0000000 00000012202 14507372607 0025605 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 scheduler
import (
"context"
"testing"
"time"
)
type testJob struct {
interval time.Duration
shouldEnable bool
startingNow bool
id string
ctr int
stopAfter int
}
func (j *testJob) Run(_ context.Context) (bool, error) {
j.ctr++
if j.ctr == j.stopAfter {
return false, nil
}
return true, nil
}
func (j *testJob) ID() string {
return j.id
}
func (j *testJob) Interval() (time.Duration, bool) {
return j.interval, j.startingNow
}
func (j *testJob) ShouldEnable(_ context.Context) bool {
return j.shouldEnable
}
func TestSchedule(t *testing.T) {
job := &testJob{
interval: time.Second / 2,
id: "test_job",
shouldEnable: true,
startingNow: true,
ctr: 0,
}
s := Get()
if err := s.ScheduleJob(context.Background(), job, false); err != nil {
t.Errorf("AddJob(%s) failed unexecptedly with error: %v", job.ID(), err)
}
s.start()
if _, ok := s.jobs[job.ID()]; !ok {
t.Errorf("Failed to schedule %s, expected an entry in scheduled jobs", job.ID())
}
time.Sleep(3 * time.Second)
s.Stop()
if job.ctr < 4 {
t.Errorf("Scheduler failed to schedule job, counter value found %d, expcted atleast 3", job.ctr)
}
}
func TestMultipleSchedules(t *testing.T) {
ctx := context.Background()
job1 := &testJob{
interval: time.Second / 2,
id: "test_job1",
shouldEnable: true,
startingNow: true,
ctr: 0,
}
job2 := &testJob{
interval: time.Second / 2,
id: "test_job2",
shouldEnable: true,
startingNow: true,
ctr: 0,
}
s := Get()
defer s.Stop()
// Schedule multiple jobs.
if err := s.ScheduleJob(ctx, job1, false); err != nil {
t.Errorf("AddJob(%s) failed unexecptedly with error: %v", job1.id, err)
}
if err := s.ScheduleJob(ctx, job2, false); err != nil {
t.Errorf("AddJob(%s) failed unexecptedly with error: %v", job2.id, err)
}
time.Sleep(2 * time.Second)
s.UnscheduleJob(job2.ID())
if _, ok := s.jobs[job1.ID()]; !ok {
t.Errorf("Failed to schedule %s, expected an entry in scheduled jobs", job1.ID())
}
if _, ok := s.jobs[job2.ID()]; ok {
t.Errorf("Failed to unschedule %s, found an entry in scheduled jobs", job2.ID())
}
time.Sleep(time.Second)
// Verify job1 is still running and job2 is unscheduled.
if job1.ctr < 4 {
t.Errorf("Scheduler failed to schedule job, counter value found %d, expcted atleast 3", job1.ctr)
}
if job2.ctr > 3 {
t.Errorf("Scheduler failed to unschedule job, counter value found %d, expcted less than 3", job2.ctr)
}
}
func TestStopSchedule(t *testing.T) {
s := Get()
job := &testJob{
interval: time.Second / 2,
id: "test_job",
shouldEnable: true,
startingNow: true,
stopAfter: 2,
ctr: 0,
}
if err := s.ScheduleJob(context.Background(), job, false); err != nil {
t.Errorf("AddJob(%s) failed unexecptedly with error: %v", job.ID(), err)
}
if _, ok := s.jobs[job.ID()]; !ok {
t.Errorf("Failed to schedule %s, expected an entry in scheduled jobs", job.ID())
}
time.Sleep(3 * time.Second)
if job.ctr > 3 {
t.Errorf("Scheduler failed to stop the job, counter value found %d, should have stopped after max 3", job.ctr)
}
}
func TestScheduleJobError(t *testing.T) {
job := &testJob{
interval: time.Second / 2,
id: "test_job",
shouldEnable: false,
}
s := Get()
if err := s.ScheduleJob(context.Background(), job, false); err == nil {
t.Errorf("ScheduleJob(ctx, %s) succeeded unexpectedly when shouldEnable set to false, want error", job.ID())
}
}
type testLongJob struct {
id string
sleepFor time.Duration
}
func (j *testLongJob) Run(_ context.Context) (bool, error) {
time.Sleep(j.sleepFor)
return false, nil
}
func (j *testLongJob) ID() string {
return j.id
}
func (j *testLongJob) Interval() (time.Duration, bool) {
return 2 * time.Minute, true
}
func (j *testLongJob) ShouldEnable(_ context.Context) bool {
return true
}
func TestScheduleJobsWait(t *testing.T) {
ctx := context.Background()
start := time.Now().Second()
ScheduleJobs(ctx, []Job{&testLongJob{id: "job1", sleepFor: time.Second}}, true)
end := time.Now().Second()
want := 1
if got := end - start; got < want {
t.Errorf("ScheduleJobs(ctx, job1, true) returned after %d seconds, expected to wait for %d", got, want)
}
}
func TestScheduleJobsNoWait(t *testing.T) {
ctx := context.Background()
start := time.Now().Second()
ScheduleJobs(ctx, []Job{&testLongJob{id: "job1", sleepFor: time.Second}}, false)
end := time.Now().Second()
if got := end - start; got >= 1 {
t.Errorf("ScheduleJobs(ctx, job1, true) returned after %d seconds, expected no wait", got)
}
}
guest-agent-20231004.02/google_guest_agent/service.go 0000664 0000000 0000000 00000005061 14507372607 0022257 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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-20231004.02/google_guest_agent/snapshot_listener.go 0000664 0000000 0000000 00000014672 14507372607 0024373 0 ustar 00root root 0000000 0000000 // Copyright 2020 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
// https://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"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
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"
"google.golang.org/grpc/credentials/insecure"
)
var (
scriptsDir = "/etc/google/snapshots/"
seenPreSnapshotOperationIds = lru.New(128)
seenPostSnapshotOperationIds = lru.New(128)
maxRequestHandleAttempts = 10 // completely arbitrary max attempt
)
type snapshotConfig struct {
timeout time.Duration // seconds
}
func getSnapshotConfig(timeoutInSeconds int) (snapshotConfig, error) {
var conf snapshotConfig
conf.timeout = time.Duration(timeoutInSeconds) * time.Second
return conf, nil
}
func runScript(ctx context.Context, 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 := run.WithOutputTimeout(ctx, config.timeout, path, disks)
if execResult.ExitCode == 124 {
return execResult.ExitCode, sspb.AgentErrorCode_SCRIPT_TIMED_OUT
}
if execResult.ExitCode != 0 {
return execResult.ExitCode, sspb.AgentErrorCode_UNHANDLED_SCRIPT_ERROR
}
return execResult.ExitCode, sspb.AgentErrorCode_NO_ERROR
}
func listenForSnapshotRequests(ctx context.Context, 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(ctx)
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(ctx context.Context, timeoutInSeconds int, 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(timeoutInSeconds)
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(ctx, url, request.GetDiskList(), config)
response.ScriptsReturnCode = int32(scriptsReturnCode)
response.AgentReturnCode = agentErrorCode
return response
default:
}
return nil
}
func handleSnapshotRequests(ctx context.Context, timeoutInSeconds int, address string, requestChan <-chan *sspb.GuestMessage) {
for {
conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()), 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(ctx, timeoutInSeconds, guestMessage)
// We either got a duplicated pre/post or an invalid request
// in both cases we want to ignore it.
if response == nil {
continue
}
for i := 0; i < maxRequestHandleAttempts; i++ {
logger.Infof("Attempt %d/%d of handling snapshot request.", i+1, maxRequestHandleAttempts)
c := sspb.NewSnapshotServiceClient(conn)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
_, err = c.HandleResponsesFromGuest(ctx, response)
if err != nil {
logger.Errorf("Error sending response: %v.", err)
time.Sleep(1 * time.Second) // Avoid idle looping
continue
}
logger.Debugf("Successfully handled snapshot request.")
break
}
}
}
}
func startSnapshotListener(ctx context.Context, snapshotServiceIP string, snapshotServicePort int, timeoutInSeconds 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(ctx, address, requestChan)
go handleSnapshotRequests(ctx, timeoutInSeconds, address, requestChan)
}
guest-agent-20231004.02/google_guest_agent/snapshot_service/ 0000775 0000000 0000000 00000000000 14507372607 0023645 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/snapshot_service/snapshot_service.proto 0000664 0000000 0000000 00000004762 14507372607 0030322 0 ustar 00root root 0000000 0000000 // Copyright 2019 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
// https://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-20231004.02/google_guest_agent/snapshot_service/snapshot_service.proto.pb.go 0000664 0000000 0000000 00000055630 14507372607 0031326 0 ustar 00root root 0000000 0000000 // 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-20231004.02/google_guest_agent/sshca/ 0000775 0000000 0000000 00000000000 14507372607 0021367 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/sshca/sshca.go 0000664 0000000 0000000 00000007030 14507372607 0023017 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 sshca is the actual writing end of the sshtrustedca pipeline.
package sshca
import (
"context"
"encoding/json"
"strings"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/events/sshtrustedca"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
// Certificates wrapps a list of certificate authorities.
type Certificates struct {
Certs []TrustedCert `json:"trustedCertificateAuthorities"`
}
// TrustedCert defines the object containing a public key.
type TrustedCert struct {
PublicKey string `json:"publicKey"`
}
var (
// cachedCertificate stores the previously retrieved certificate to be cached in case mds fails.
cachedCertificate string
// mdsClient is the metadata's client, used to query oslogin certificates.
mdsClient *metadata.Client
)
// Init initializes the sshca's event handler callback.
func Init(eventManager *events.Manager) {
mdsClient = metadata.New()
eventManager.Subscribe(sshtrustedca.ReadEvent, nil, writeFile)
}
// writeFile is an event handler callback and writes the actual sshca content to the pipe
// used by openssh to grant access based on ssh ca.
func writeFile(ctx context.Context, evType string, data interface{}, evData *events.EventData) bool {
// There was some error on the pipe watcher, just ignore it.
if evData.Error != nil {
logger.Debugf("Not handling ssh trusted ca cert event, we got an error: %+v", evData.Error)
return true
}
// Make sure we close the pipe after we've done writing to it.
pipeData := evData.Data.(*sshtrustedca.PipeData)
defer func() {
if err := pipeData.File.Close(); err != nil {
logger.Errorf("Failed to close pipe: %+v", err)
}
pipeData.Finished()
}()
// The certificates key/endpoint is not cached, we can't rely on the metadata watcher data because of that.
certificate, err := mdsClient.GetKey(ctx, "oslogin/certificates", nil)
if err != nil && cachedCertificate != "" {
certificate = cachedCertificate
logger.Warningf("Failed to get certificate, assuming/using previously cached one.")
} else if err != nil {
logger.Errorf("Failed to get certificate from metadata server: %+v", err)
return true
}
// Keep a copy of the returned certificate for error fallback caching.
cachedCertificate = certificate
var certs Certificates
var outData []string
if err := json.Unmarshal([]byte(certificate), &certs); err != nil {
logger.Errorf("Failed to unmarshal certificate json: %+v", err)
return true
}
for _, curr := range certs.Certs {
outData = append(outData, curr.PublicKey)
}
outStr := strings.Join(outData, "\n")
n, err := pipeData.File.WriteString(outStr)
if err != nil {
logger.Errorf("Failed to write certificate to the write end of the pipe: %+v", err)
return true
}
if n != len(outStr) {
logger.Errorf("Wrote the wrong ammout of data, wrote %d bytes instead of %d bytes", n, len(certificate))
}
return true
}
guest-agent-20231004.02/google_guest_agent/stub.go 0000664 0000000 0000000 00000002413 14507372607 0021572 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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.
//go:build !windows
// +build !windows
package main
import (
"context"
"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(ctx context.Context, path string) (versionInfo, error) {
return versionInfo{0, 0}, nil
}
func checkWindowsServiceRunning(ctx context.Context, servicename string) bool {
return false
}
guest-agent-20231004.02/google_guest_agent/system_windows.go 0000664 0000000 0000000 00000006270 14507372607 0023720 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"strings"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
"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(ctx context.Context, servicename string) bool {
res := run.WithOutput(ctx, "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(ctx context.Context, path string) (versionInfo, error) {
psCmd := "(Get-Item '" + path + "').VersionInfo.FileVersion"
res := run.WithOutput(ctx, "powershell", "-c", psCmd)
if res.ExitCode != 0 {
return versionInfo{0, 0}, fmt.Errorf(res.Error())
}
return parseVersionInfo([]byte(res.StdOut))
}
guest-agent-20231004.02/google_guest_agent/telemetry/ 0000775 0000000 0000000 00000000000 14507372607 0022300 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/telemetry/proto/ 0000775 0000000 0000000 00000000000 14507372607 0023443 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/telemetry/proto/telemetry.pb.go 0000664 0000000 0000000 00000023606 14507372607 0026413 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.12
// source: telemetry.proto
package telemetry
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type OSInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
OsType *string `protobuf:"bytes,1,opt,name=os_type,json=osType,proto3,oneof" json:"os_type,omitempty"`
LongName *string `protobuf:"bytes,2,opt,name=long_name,json=longName,proto3,oneof" json:"long_name,omitempty"`
ShortName *string `protobuf:"bytes,3,opt,name=short_name,json=shortName,proto3,oneof" json:"short_name,omitempty"`
Version *string `protobuf:"bytes,4,opt,name=version,proto3,oneof" json:"version,omitempty"`
KernelVersion *string `protobuf:"bytes,5,opt,name=kernel_version,json=kernelVersion,proto3,oneof" json:"kernel_version,omitempty"`
KernelRelease *string `protobuf:"bytes,6,opt,name=kernel_release,json=kernelRelease,proto3,oneof" json:"kernel_release,omitempty"`
}
func (x *OSInfo) Reset() {
*x = OSInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_telemetry_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *OSInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OSInfo) ProtoMessage() {}
func (x *OSInfo) ProtoReflect() protoreflect.Message {
mi := &file_telemetry_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use OSInfo.ProtoReflect.Descriptor instead.
func (*OSInfo) Descriptor() ([]byte, []int) {
return file_telemetry_proto_rawDescGZIP(), []int{0}
}
func (x *OSInfo) GetOsType() string {
if x != nil && x.OsType != nil {
return *x.OsType
}
return ""
}
func (x *OSInfo) GetLongName() string {
if x != nil && x.LongName != nil {
return *x.LongName
}
return ""
}
func (x *OSInfo) GetShortName() string {
if x != nil && x.ShortName != nil {
return *x.ShortName
}
return ""
}
func (x *OSInfo) GetVersion() string {
if x != nil && x.Version != nil {
return *x.Version
}
return ""
}
func (x *OSInfo) GetKernelVersion() string {
if x != nil && x.KernelVersion != nil {
return *x.KernelVersion
}
return ""
}
func (x *OSInfo) GetKernelRelease() string {
if x != nil && x.KernelRelease != nil {
return *x.KernelRelease
}
return ""
}
type AgentInfo struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name *string `protobuf:"bytes,1,opt,name=name,proto3,oneof" json:"name,omitempty"`
Version *string `protobuf:"bytes,2,opt,name=version,proto3,oneof" json:"version,omitempty"`
Architecture *string `protobuf:"bytes,3,opt,name=architecture,proto3,oneof" json:"architecture,omitempty"`
}
func (x *AgentInfo) Reset() {
*x = AgentInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_telemetry_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AgentInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AgentInfo) ProtoMessage() {}
func (x *AgentInfo) ProtoReflect() protoreflect.Message {
mi := &file_telemetry_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AgentInfo.ProtoReflect.Descriptor instead.
func (*AgentInfo) Descriptor() ([]byte, []int) {
return file_telemetry_proto_rawDescGZIP(), []int{1}
}
func (x *AgentInfo) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *AgentInfo) GetVersion() string {
if x != nil && x.Version != nil {
return *x.Version
}
return ""
}
func (x *AgentInfo) GetArchitecture() string {
if x != nil && x.Architecture != nil {
return *x.Architecture
}
return ""
}
var File_telemetry_proto protoreflect.FileDescriptor
var file_telemetry_proto_rawDesc = []byte{
0x0a, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x09, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x22, 0xbe, 0x02, 0x0a,
0x06, 0x4f, 0x53, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x07, 0x6f, 0x73, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x6f, 0x73, 0x54, 0x79,
0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x08, 0x6c, 0x6f, 0x6e, 0x67,
0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x73, 0x68, 0x6f, 0x72, 0x74,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x09, 0x73,
0x68, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x6b, 0x65,
0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x48, 0x04, 0x52, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c,
0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05,
0x52, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x88,
0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6f, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x0c,
0x0a, 0x0a, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x0a, 0x0b,
0x5f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6b, 0x65, 0x72, 0x6e,
0x65, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x6b,
0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x22, 0x92, 0x01,
0x0a, 0x09, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74,
0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0c, 0x61, 0x72, 0x63,
0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75,
0x72, 0x65, 0x42, 0x1e, 0x5a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5f, 0x67, 0x75, 0x65,
0x73, 0x74, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74,
0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_telemetry_proto_rawDescOnce sync.Once
file_telemetry_proto_rawDescData = file_telemetry_proto_rawDesc
)
func file_telemetry_proto_rawDescGZIP() []byte {
file_telemetry_proto_rawDescOnce.Do(func() {
file_telemetry_proto_rawDescData = protoimpl.X.CompressGZIP(file_telemetry_proto_rawDescData)
})
return file_telemetry_proto_rawDescData
}
var file_telemetry_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_telemetry_proto_goTypes = []interface{}{
(*OSInfo)(nil), // 0: telemetry.OSInfo
(*AgentInfo)(nil), // 1: telemetry.AgentInfo
}
var file_telemetry_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_telemetry_proto_init() }
func file_telemetry_proto_init() {
if File_telemetry_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_telemetry_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*OSInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_telemetry_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AgentInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_telemetry_proto_msgTypes[0].OneofWrappers = []interface{}{}
file_telemetry_proto_msgTypes[1].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_telemetry_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_telemetry_proto_goTypes,
DependencyIndexes: file_telemetry_proto_depIdxs,
MessageInfos: file_telemetry_proto_msgTypes,
}.Build()
File_telemetry_proto = out.File
file_telemetry_proto_rawDesc = nil
file_telemetry_proto_goTypes = nil
file_telemetry_proto_depIdxs = nil
}
guest-agent-20231004.02/google_guest_agent/telemetry/proto/telemetry.proto 0000664 0000000 0000000 00000001771 14507372607 0026550 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 telemetry;
option go_package = "google_guest_agent/telemetry";
message OSInfo {
optional string os_type = 1;
optional string long_name = 2;
optional string short_name = 3;
optional string version = 4;
optional string kernel_version = 5;
optional string kernel_release = 6;
}
message AgentInfo {
optional string name = 1;
optional string version = 2;
optional string architecture = 3;
}
guest-agent-20231004.02/google_guest_agent/telemetry/telemetry.go 0000664 0000000 0000000 00000010266 14507372607 0024646 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 telemetry
import (
"context"
"encoding/base64"
"runtime"
"time"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"google.golang.org/protobuf/proto"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/osinfo"
tpb "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/telemetry/proto"
)
var (
telemetryJobID = "telemetryJobID"
telemetryInterval = 24 * time.Hour
)
// Data is telemetry data on the current agent and OS.
type Data struct {
// Name of the agent.
AgentName string
// Version of the Agent.
AgentVersion string
// Architecture of the Agent.
AgentArch string
// OS name.
OS string
// The name the OS uses to fully describe itself.
LongName string
// OS name in short form (aka distro name).
ShortName string
// Version of the OS.
Version string
// Kernel Release.
KernelRelease string
// Kernel Version.
KernelVersion string
}
func formatGuestAgent(d Data) string {
data, err := proto.Marshal(&tpb.AgentInfo{
Name: &d.AgentName,
Version: &d.AgentVersion,
Architecture: &d.AgentArch,
})
if err != nil {
logger.Warningf("Error marshalling AgentInfo: %v", err)
}
return base64.StdEncoding.EncodeToString(data)
}
func formatGuestOS(d Data) string {
data, err := proto.Marshal(&tpb.OSInfo{
OsType: &d.OS,
LongName: &d.LongName,
ShortName: &d.ShortName,
Version: &d.Version,
KernelVersion: &d.KernelVersion,
KernelRelease: &d.KernelRelease,
})
if err != nil {
logger.Warningf("Error marshalling AgentInfo: %v", err)
}
return base64.StdEncoding.EncodeToString(data)
}
// Record records telemetry data.
func Record(ctx context.Context, client metadata.MDSClientInterface, d Data) error {
headers := map[string]string{
"X-Google-Guest-Agent": formatGuestAgent(d),
"X-Google-Guest-OS": formatGuestOS(d),
}
// This is the simplest metadata call we can make, and we dont care about any return value,
// all we need to do is make some call with the telemetry headers.
_, err := client.GetKey(ctx, "", headers)
return err
}
// Job implements job scheduler interface for recording telemetry.
type Job struct {
client metadata.MDSClientInterface
programName string
agentVersion string
}
// New initializes a new TelemetryJob.
func New(client metadata.MDSClientInterface, programName, agentVersion string) *Job {
return &Job{
client: client,
programName: programName,
agentVersion: agentVersion,
}
}
// ID returns the ID for this job.
func (j *Job) ID() string {
return telemetryJobID
}
// Run records telemetry data.
func (j *Job) Run(ctx context.Context) (bool, error) {
osInfo := osinfo.Get()
d := Data{
AgentName: j.programName,
AgentVersion: j.agentVersion,
AgentArch: runtime.GOARCH,
OS: runtime.GOOS,
LongName: osInfo.PrettyName,
ShortName: osInfo.OS,
Version: osInfo.VersionID,
KernelRelease: osInfo.KernelRelease,
KernelVersion: osInfo.KernelVersion,
}
if err := Record(ctx, j.client, d); err != nil {
// Log this here in Debug mode as telemetry is best effort.
logger.Debugf("Error recording telemetry: %v", err)
}
return j.ShouldEnable(ctx), nil
}
// Interval returns the interval at which job is executed.
func (j *Job) Interval() (time.Duration, bool) {
return telemetryInterval, true
}
// ShouldEnable returns true as long as DisableTelemetry is not set in metadata.
func (j *Job) ShouldEnable(ctx context.Context) bool {
md, err := j.client.Get(ctx)
if err != nil {
return false
}
return !md.Instance.Attributes.DisableTelemetry && !md.Project.Attributes.DisableTelemetry
}
guest-agent-20231004.02/google_guest_agent/telemetry/telemetry_test.go 0000664 0000000 0000000 00000003207 14507372607 0025702 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 telemetry
import (
"context"
"testing"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/fakes"
)
type mdsClient struct {
getKeyHeaders map[string]string
fakes.MDSClient
}
func (c *mdsClient) GetKey(ctx context.Context, key string, headers map[string]string) (string, error) {
c.getKeyHeaders = headers
return "", nil
}
func TestRecord(t *testing.T) {
client := &mdsClient{}
d := Data{
AgentVersion: "AgentVersion",
AgentArch: "AgentArch",
OS: "OS",
LongName: "LongName",
ShortName: "ShortName",
Version: "Version",
KernelRelease: "KernelRelease",
KernelVersion: "KernelVersion",
}
if err := Record(context.Background(), client, d); err != nil {
t.Fatalf("Error running Record: %v", err)
}
want := map[string]string{
"X-Google-Guest-Agent": formatGuestAgent(d),
"X-Google-Guest-OS": formatGuestOS(d),
}
got := client.getKeyHeaders
for k, v := range want {
if got[k] != v {
t.Errorf("received headers does not contain all expected headers, want: %q, got: %q", want, got)
}
}
}
guest-agent-20231004.02/google_guest_agent/uefi/ 0000775 0000000 0000000 00000000000 14507372607 0021216 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_guest_agent/uefi/uefi.go 0000664 0000000 0000000 00000002002 14507372607 0022467 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 uefi provides utility functions to read UEFI variables.
package uefi
// VariableName represents UEFI variable name and GUID.
// Format: {VariableName}-{VendorGUID}
type VariableName struct {
RootDir string
Name string
GUID string
}
// Variable represents UEFI Variable and its contents.
// Attributes are not set in case of Windows.
type Variable struct {
Name VariableName
Attributes []byte
Content []byte
}
guest-agent-20231004.02/google_guest_agent/uefi/uefi_unix.go 0000664 0000000 0000000 00000003056 14507372607 0023544 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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.
//go:build unix
// Package uefi provides utility functions to read UEFI variables.
package uefi
import (
"fmt"
"os"
"path/filepath"
)
const (
defaultEFIVarsDir = "/sys/firmware/efi/efivars"
)
// Path returns a path for UEFI variable on disk.
func (v VariableName) Path() string {
root := v.RootDir
if root == "" {
root = defaultEFIVarsDir
}
return filepath.Join(root, v.Name+"-"+v.GUID)
}
// ReadVariable reads UEFI variable and returns as byte array.
// Throws an error if variable is invalid or empty.
func ReadVariable(v VariableName) (*Variable, error) {
path := v.Path()
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("error reading %q: %v", path, err)
}
// According to UEFI specification the first four bytes of the contents are attributes.
if len(b) < 4 {
return nil, fmt.Errorf("%q contains %d bytes of data, it should have at least 4", path, len(b))
}
return &Variable{
Name: v,
Attributes: b[:4],
Content: b[4:],
}, nil
}
guest-agent-20231004.02/google_guest_agent/uefi/uefi_unix_test.go 0000664 0000000 0000000 00000004311 14507372607 0024576 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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.
//go:build unix
package uefi
import (
"os"
"path/filepath"
"testing"
)
func TestVariablePath(t *testing.T) {
v := VariableName{Name: "name", GUID: "guid"}
want := "/sys/firmware/efi/efivars/name-guid"
if got := v.Path(); got != want {
t.Errorf("VariablePath(%+v) = %v, want %v", v, got, want)
}
}
func TestReadVariable(t *testing.T) {
root := t.TempDir()
v := VariableName{Name: "testname", GUID: "testguid", RootDir: root}
fakecert := `
-----BEGIN CERTIFICATE-----
sdfsd
-----END CERTIFICATE-----
`
fakeUefi := []byte("attr" + fakecert)
path := filepath.Join(root, "testname-testguid")
if err := os.WriteFile(path, fakeUefi, 0644); err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
defer os.Remove(path)
got, err := ReadVariable(v)
if err != nil {
t.Errorf("ReadVariable(%+v) failed unexpectedly with error: %v", v, err)
}
if string(got.Attributes) != "attr" {
t.Errorf("ReadVariable(%+v) = %s as attributes, want %s", v, string(got.Attributes), "attr")
}
if string(got.Content) != fakecert {
t.Errorf("ReadVariable(%+v) = %s as content, want %s", v, string(got.Content), fakeUefi)
}
}
func TestReadVariableError(t *testing.T) {
root := t.TempDir()
v := VariableName{Name: "testname", GUID: "testguid", RootDir: root}
p := filepath.Join(root, "testname-testguid")
// File not exist error.
_, err := ReadVariable(v)
if err == nil {
t.Errorf("ReadVariable(%+v) succeeded for non-existent file, want error", v)
}
// Empty variable error.
os.WriteFile(p, []byte(""), 0644)
_, err = ReadVariable(v)
if err == nil {
t.Errorf("ReadVariable(%+v) succeeded for invalid format, want error", v)
}
}
guest-agent-20231004.02/google_guest_agent/uefi/uefi_windows.go 0000664 0000000 0000000 00000015257 14507372607 0024261 0 ustar 00root root 0000000 0000000 // Copyright 2023 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
// https://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 uefi provides utility functions to read UEFI variables.
package uefi
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
)
var (
// https://en.wikipedia.org/wiki/Microsoft_Windows_library_files#KERNEL32.DLL
kernelDLL = windows.NewLazySystemDLL("kernel32.dll")
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess
// procGetCurrentProcess retrieves a pseudo handle for the current process.
procGetCurrentProcess = kernelDLL.NewProc("GetCurrentProcess")
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
// procCloseHandle closes an open process object handle.
procCloseHandle = kernelDLL.NewProc("CloseHandle")
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getfirmwareenvironmentvariablew
// procGetFirmwareEnvironmentVariableW retrieves the value of the specified UEFI.
procGetFirmwareEnvironmentVariableW = kernelDLL.NewProc("GetFirmwareEnvironmentVariableW")
// https://en.wikipedia.org/wiki/Microsoft_Windows_library_files#ADVAPI32.DLL
advapiDLL = windows.NewLazySystemDLL("advapi32.dll")
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocesstoken
// procOpenProcessToken opens the access token (contains the security information for a logon session) associated for a process.
// Token identifies the user, the user's groups, and the user's privileges.
procOpenProcessToken = advapiDLL.NewProc("OpenProcessToken")
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-lookupprivilegevaluew
// procLookupPrivilegeValueW is used to retrieve the locally unique identifier (LUID)
procLookupPrivilegeValueW = advapiDLL.NewProc("LookupPrivilegeValueW")
// https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-adjusttokenprivileges
// procAdjustTokenPrivileges is used for enabling the privileges on the access token.
procAdjustTokenPrivileges = advapiDLL.NewProc("AdjustTokenPrivileges")
)
const (
// SE_SYSTEM_ENVIRONMENT_NAME is the privilege required to read a firmware environment variable.
SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege"
// PROC_TOKEN_ADJUST_PRIVILEGES is access required to change the specified privileges.
PROC_TOKEN_ADJUST_PRIVILEGES = 0x0020
// PROC_SE_PRIVILEGE_ENABLED is privilege attribute used with LUID_AND_ATTRIBUTES stating to
// enable the specified privilege.
PROC_SE_PRIVILEGE_ENABLED = 0x00000002
)
// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-luid
// LUID is the opaque identifier structure that is guaranteed to be unique on the local machine.
// It is used to locally represent the privilege name (e.g. SeSystemEnvironmentPrivilege in this case).
type LUID struct {
LowPart uint32
HighPart int32
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-luid_and_attributes
// LUID_AND_ATTRIBUTES structure represents a locally unique identifier (LUID) and its attributes.
type LUID_AND_ATTRIBUTES struct {
LUID LUID
Attributes uint32
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_privileges
// TOKEN_PRIVILEGES is the structure that contains information about a set of privileges for an access token.
type TOKEN_PRIVILEGES struct {
PrivilegeCount uint32
Privileges [1]LUID_AND_ATTRIBUTES
}
// ReadVariable reads UEFI variable and returns as byte array.
// Throws an error if variable is invalid or empty.
func ReadVariable(v VariableName) (*Variable, error) {
logger.Debugf("Enabling required %s priviliges for agent process", SE_SYSTEM_ENVIRONMENT_NAME)
if err := enablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME); err != nil {
return nil, err
}
name := unsafe.Pointer(syscall.StringToUTF16Ptr(v.Name))
guid := unsafe.Pointer(syscall.StringToUTF16Ptr("{" + v.GUID + "}"))
buffer := make([]byte, 1024)
// This call returns number of bytes written to the output buffer, unused, error
size, _, err := procGetFirmwareEnvironmentVariableW.Call(
uintptr(name),
uintptr(guid),
uintptr(unsafe.Pointer(&buffer[0])),
uintptr(uint32(len(buffer))),
)
if size == uintptr(0) {
return nil, fmt.Errorf("unable to read UEFI variable %+v: %w", v, err)
}
return &Variable{
Name: v,
Attributes: []byte{},
Content: buffer[:size],
}, nil
}
// enablePrivilege enables the specified privilege for current process.
func enablePrivilege(name string) error {
// Get current process handle.
handle, _, err := procGetCurrentProcess.Call()
if handle == uintptr(0) {
return fmt.Errorf("unable to get current process handle: %w", err)
}
defer procCloseHandle.Call(handle)
// Get access token that contains the privileges to be modified for the current process.
var tHandle uintptr
opRes, _, err := procOpenProcessToken.Call(
uintptr(handle),
uintptr(uint32(PROC_TOKEN_ADJUST_PRIVILEGES)),
uintptr(unsafe.Pointer(&tHandle)),
)
if opRes == uintptr(0) {
return fmt.Errorf("unable to open current process token: %w", err)
}
defer procCloseHandle.Call(tHandle)
// Generate a pointer to a null-terminated string that specifies the name of the privilege.
namePtr, err := syscall.UTF16PtrFromString(name)
if err != nil {
return fmt.Errorf("unable to encode privilege name(%s) to UTF16: %w", name, err)
}
// Retrieve the LUID for the required privilege.
var luid LUID
lpRes, _, err := procLookupPrivilegeValueW.Call(
uintptr(0),
uintptr(unsafe.Pointer(namePtr)),
uintptr(unsafe.Pointer(&luid)),
)
if lpRes == uintptr(0) {
return fmt.Errorf("unable to lookup LUID for privilege %q: %w", name, err)
}
newState := TOKEN_PRIVILEGES{PrivilegeCount: 1}
newState.Privileges[0] = LUID_AND_ATTRIBUTES{
LUID: luid,
Attributes: PROC_SE_PRIVILEGE_ENABLED,
}
// Enable specified privilege on the current process.
ajRes, _, err := procAdjustTokenPrivileges.Call(
uintptr(tHandle),
uintptr(uint32(0)),
uintptr(unsafe.Pointer(&newState)),
uintptr(uint32(0)),
uintptr(0),
uintptr(0),
)
if ajRes == uintptr(0) {
return fmt.Errorf("unable to set privilege %q: %w", name, err)
}
return nil
}
guest-agent-20231004.02/google_guest_agent/windows_accounts.go 0000664 0000000 0000000 00000027757 14507372607 0024230 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/json"
"fmt"
"hash"
"math/big"
"reflect"
"runtime"
"strconv"
"strings"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"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
}
func createOrResetPwd(ctx context.Context, k metadata.WindowsKey) (*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 {
if err := addUserToGroup(ctx, 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(ctx, k.UserName, pwd); err != nil {
return nil, fmt.Errorf("error running createUser: %v", err)
}
if k.AddToAdministrators == nil || *k.AddToAdministrators {
if err := addUserToGroup(ctx, k.UserName, "Administrators"); err != nil {
return nil, fmt.Errorf("error running addUserToGroup: %v", err)
}
}
}
return createcredsJSON(k, pwd)
}
func createSSHUser(ctx context.Context, 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(ctx, user, pwd); err != nil {
return fmt.Errorf("error running createUser: %v", err)
}
if err := addUserToGroup(ctx, user, "Administrators"); err != nil {
return fmt.Errorf("error running addUserToGroup: %v", err)
}
return nil
}
func createcredsJSON(k metadata.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.Descriptor) 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 {
// fakeWindows forces Disabled to run as if it was running in a windows system.
// mostly target for unit tests.
fakeWindows bool
}
func (a *winAccountsMgr) Diff(ctx context.Context) (bool, error) {
oldSSHEnable := getWinSSHEnabled(oldMetadata)
sshEnable := getWinSSHEnabled(newMetadata)
if sshEnable != oldSSHEnable {
return true, nil
}
if !reflect.DeepEqual(newMetadata.Instance.Attributes.WindowsKeys, oldMetadata.Instance.Attributes.WindowsKeys) {
return true, nil
}
if !compareStringSlice(newMetadata.Instance.Attributes.SSHKeys, oldMetadata.Instance.Attributes.SSHKeys) {
return true, nil
}
if !compareStringSlice(newMetadata.Project.Attributes.SSHKeys, oldMetadata.Project.Attributes.SSHKeys) {
return true, nil
}
if newMetadata.Instance.Attributes.BlockProjectKeys != oldMetadata.Instance.Attributes.BlockProjectKeys {
return true, nil
}
return false, nil
}
func (a *winAccountsMgr) Timeout(ctx context.Context) (bool, error) {
return false, nil
}
func (a *winAccountsMgr) Disabled(ctx context.Context) (bool, error) {
if !a.fakeWindows && runtime.GOOS != "windows" {
return true, nil
}
config := cfg.Get()
if config.AccountManager != nil {
return config.AccountManager.Disable, nil
}
if newMetadata.Instance.Attributes.DisableAccountManager != nil {
return *newMetadata.Instance.Attributes.DisableAccountManager, nil
}
if newMetadata.Project.Attributes.DisableAccountManager != nil {
return *newMetadata.Project.Attributes.DisableAccountManager, nil
}
return false, nil
}
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(ctx context.Context) error {
sshdPath, err := getWindowsServiceImagePath(sshdRegKey)
if err != nil {
return fmt.Errorf("cannot determine sshd path: %v", err)
}
sshdVersion, err := getWindowsExeVersion(ctx, sshdPath)
if err != nil {
return fmt.Errorf("cannot determine OpenSSH Version: %v", err)
}
return versionOk(sshdVersion, minSSHVersion)
}
func (a *winAccountsMgr) Set(ctx context.Context) error {
oldSSHEnable := getWinSSHEnabled(oldMetadata)
sshEnable := getWinSSHEnabled(newMetadata)
if sshEnable {
if sshEnable != oldSSHEnable {
err := verifyWinSSHVersion(ctx)
if err != nil {
logger.Warningf(err.Error())
}
if !checkWindowsServiceRunning(ctx, "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(ctx, 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 := createOrResetPwd(ctx, key)
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 metadata.WindowsKeys, oldStrKeys []string) metadata.WindowsKeys {
if len(newKeys) == 0 {
return nil
}
if len(oldStrKeys) == 0 {
return newKeys
}
var oldKeys metadata.WindowsKeys
for _, s := range oldStrKeys {
var key metadata.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 metadata.WindowsKeys
for _, key := range newKeys {
if func(key metadata.WindowsKey, oldKeys metadata.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-20231004.02/google_guest_agent/windows_accounts_test.go 0000664 0000000 0000000 00000031664 14507372607 0025257 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"hash"
"math/big"
"reflect"
"testing"
"time"
"unicode"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-agent/utils"
)
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 := metadata.WindowsKey{ExpireOn: tt.sTime}
expired, _ := utils.CheckExpired(k.ExpireOn)
if tt.e != 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.Descriptor
want bool
}{
{"not explicitly disabled", []byte(""), &metadata.Descriptor{}, false},
{"enabled in cfg only", []byte("[accountManager]\ndisable=false"), &metadata.Descriptor{}, false},
{"disabled in cfg only", []byte("[accountManager]\ndisable=true"), &metadata.Descriptor{}, true},
{"disabled in cfg, enabled in instance metadata", []byte("[accountManager]\ndisable=true"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAccountManager: mkptr(false)}}}, true},
{"enabled in cfg, disabled in instance metadata", []byte("[accountManager]\ndisable=false"), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAccountManager: mkptr(true)}}}, false},
{"enabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAccountManager: mkptr(false)}}}, false},
{"enabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{DisableAccountManager: mkptr(false)}}}, false},
{"disabled in instance metadata only", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAccountManager: mkptr(true)}}}, true},
{"enabled in instance metadata, disabled in project metadata", []byte(""), &metadata.Descriptor{Instance: metadata.Instance{Attributes: metadata.Attributes{DisableAccountManager: mkptr(false)}}, Project: metadata.Project{Attributes: metadata.Attributes{DisableAccountManager: mkptr(true)}}}, false},
{"disabled in project metadata only", []byte(""), &metadata.Descriptor{Project: metadata.Project{Attributes: metadata.Attributes{DisableAccountManager: mkptr(true)}}}, true},
}
ctx := context.Background()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reloadConfig(t, tt.data)
newMetadata = tt.md
mgr := &winAccountsMgr{
fakeWindows: true,
}
got, err := mgr.Disabled(ctx)
if err != nil {
t.Errorf("Failed to run winAccountsMgr's Disabled() call: %+v", err)
}
if got != tt.want {
t.Errorf("accounts.disabled() got: %t, want: %t", got, tt.want)
}
})
}
reloadConfig(t, nil)
got, err := (&winAccountsMgr{}).Disabled(ctx)
if err != nil {
t.Errorf("Failed to run winAccountsMgr's Disabled() call: %+v", err)
}
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 := metadata.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 metadata.WindowsKeys
oldStrKeys []string
wantAdd metadata.WindowsKeys
}{
// These should return toAdd:
// In MD, not Reg
{metadata.WindowsKeys{{UserName: "foo"}}, nil, metadata.WindowsKeys{{UserName: "foo"}}},
{metadata.WindowsKeys{{UserName: "foo"}}, []string{`{"UserName":"bar"}`}, metadata.WindowsKeys{{UserName: "foo"}}},
// These should return nothing:
// In Reg and MD
{metadata.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 TestRemoveExpiredKeys(t *testing.T) {
var tests = []struct {
key string
valid bool
}{
{`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0000"}`, true},
{`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0700"}`, true},
{`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0700", "futureField": "UNUSED_FIELDS_IGNORED"}`, true},
{`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2018-11-08T19:30:46+0000"}`, false},
{`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2018-11-08T19:30:46+0700"}`, false},
{`user:ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"INVALID_TIMESTAMP"}`, false},
{`user:ssh-rsa [KEY] google-ssh`, false},
{`user:ssh-rsa [KEY] user`, true},
{`user:ssh-rsa [KEY]`, true},
// having the user: prefix should not affect whether a key is expired, repeat test cases without user: prefix
{`ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0000"}`, true},
{`ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0700"}`, true},
{`ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2028-11-08T19:30:47+0700", "futureField": "UNUSED_FIELDS_IGNORED"}`, true},
{`ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2018-11-08T19:30:46+0000"}`, false},
{`ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"2018-11-08T19:30:46+0700"}`, false},
{`ssh-rsa [KEY] google-ssh {"userName":"user@email.com", "expireOn":"INVALID_TIMESTAMP"}`, false},
{`ssh-rsa [KEY] google-ssh`, false},
{`ssh-rsa [KEY] user`, true},
{`ssh-rsa [KEY]`, true},
{},
}
for _, tt := range tests {
ret := removeExpiredKeys([]string{tt.key})
if tt.valid {
if len(ret) == 0 || ret[0] != tt.key {
t.Errorf("valid key was removed: %q", tt.key)
}
}
if !tt.valid && len(ret) == 1 {
t.Errorf("invalid key was kept: %q", tt.key)
}
}
}
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-20231004.02/google_guest_agent/wsfc.go 0000664 0000000 0000000 00000016075 14507372607 0021570 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"net"
"strings"
"sync"
"time"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
"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
config := cfg.Get()
if func() bool {
if config.WSFC != nil && config.WSFC.Enable && config.WSFC.Addresses != "" {
return config.WSFC.Enable
}
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
if config.WSFC != nil && config.WSFC.Port != "" {
newPort = config.WSFC.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(ctx context.Context) (bool, error) {
return m.agentNewState != m.agent.getState() || m.agentNewPort != m.agent.getPort(), nil
}
// 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(ctx context.Context) (bool, error) {
return false, nil
}
func (m *wsfcManager) Timeout(ctx context.Context) (bool, error) {
return false, nil
}
// 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(ctx context.Context) 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 started. 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-20231004.02/google_guest_agent/wsfc_test.go 0000664 0000000 0000000 00000021324 14507372607 0022620 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"context"
"errors"
"fmt"
"net"
"reflect"
"testing"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
)
func setEnableWSFC(md metadata.Descriptor, enabled *bool) *metadata.Descriptor {
md.Instance.Attributes.EnableWSFC = enabled
return &md
}
func setWSFCAddresses(md metadata.Descriptor, wsfcAddresses string) *metadata.Descriptor {
md.Instance.Attributes.WSFCAddresses = wsfcAddresses
return &md
}
func setWSFCAgentPort(md metadata.Descriptor, wsfcPort string) *metadata.Descriptor {
md.Instance.Attributes.WSFCAgentPort = wsfcPort
return &md
}
var (
testAgent = getWsfcAgentInstance()
testMetadata = metadata.Descriptor{}
testListener = &net.TCPListener{}
)
func TestNewWsfcManager(t *testing.T) {
type args struct {
newMetadata *metadata.Descriptor
}
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}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
newMetadata = tt.args.newMetadata
if got := newWsfcManager(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("newWsfcManager() = %v, want %v", 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},
}
ctx := context.Background()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.m.Diff(ctx)
if err != nil {
t.Errorf("Failed to run wsfcManager's Diff() call: %+v", err)
}
if 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},
}
ctx := context.Background()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.m.Set(ctx); (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) {
ctx := context.Background()
wsfcMgr := &wsfcManager{
agentNewState: running,
agentNewPort: wsfcDefaultAgentPort,
agent: getWsfcAgentInstance(),
}
if err := wsfcMgr.Set(ctx); err != nil {
t.Errorf("Failed to run wsfcManager's Set() call: %+v", err)
}
// 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(),
}
if err := wsfcMgrStop.Set(ctx); err != nil {
t.Errorf("Failed to run wsfcMgr's Set() call: %+v", err)
}
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-20231004.02/google_metadata_script_runner/ 0000775 0000000 0000000 00000000000 14507372607 0022516 5 ustar 00root root 0000000 0000000 guest-agent-20231004.02/google_metadata_script_runner/main.go 0000664 0000000 0000000 00000033035 14507372607 0023775 0 ustar 00root root 0000000 0000000 // Copyright 2017 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
// https://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"
"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:///