internal/metadata/unmarshal.go (209 lines of code) (raw):

// Copyright 2024 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 ( "encoding/json" "strconv" "strings" "github.com/GoogleCloudPlatform/galog" "github.com/GoogleCloudPlatform/google-guest-agent/internal/utils/ssh" ) // descriptor wraps/holds all the metadata keys, the structure reflects the json // descriptor returned with metadata call with alt=jason. This is the internal // representation used to parse the json formatted output of metadata server. type descriptor struct { Instance instance Project project } // UnmarshalDescriptor unmarshals a jason into Descriptor. func UnmarshalDescriptor(jsonData string) (*Descriptor, error) { var data descriptor if err := json.Unmarshal([]byte(jsonData), &data); err != nil { return nil, err } return newDescriptor(data), nil } // UnmarshalJSON unmarshals b into Descriptor. This is the internal // representation used to parse the json formatted output of metadata server. func (m *descriptor) UnmarshalJSON(b []byte) error { // We can't unmarshal into metadata directly as it would create an infinite loop. type temp descriptor var t temp if err := json.Unmarshal(b, &t); err != nil { return err } *m = descriptor(t) return nil } // virtualClock is used as identifier for syncing the software clock with the // hypervisor clock. This is the internal representation used to parse the json // formatted output of metadata server. type virtualClock struct { DriftToken string } // instance describes the metadata's instance attributes/keys. This is the // internal representation used to parse the json formatted output of metadata // server. type instance struct { ID json.Number MachineType string Attributes attributes NetworkInterfaces []networkInterface VlanInterfaces []map[int]vlanInterface VirtualClock virtualClock Name string // We don't need the details for now, but we need to keep the key to know // if service accounts are present. ServiceAccounts map[string]json.RawMessage } // networkInterfaces describes the instances network interfaces configurations. // This is the internal representation used to parse the json formatted output // of metadata server. type networkInterface struct { ForwardedIps []string ForwardedIpv6s []string TargetInstanceIps []string IPAliases []string Mac string DHCPv6Refresh string } // vlanInterface describes the instances vlan network interfaces configurations. type vlanInterface struct { // Mac is the vLAN interface's mac address. Mac string // ParentInterface is the mds reference of the parent/physical interface i.e.: // /computeMetadata/v1/instance/network-interfaces/0/ ParentInterface string // Vlan is the vlan id. Vlan int // MTU is the vlan's MTU value. MTU int // IP is the vlan's ip address. IP string // IPv6 is the vlan's ipv6 address. IPv6 []string // Gateway is the vlan's gateway address. Gateway string // GatewayIPv6 is the vlan's IPv6 gateway address. GatewayIPv6 string } // project describes the projects instance's attributes. This is the internal // representation used to parse the json formatted output of metadata server. type project struct { Attributes attributes ID string `json:"projectId"` NumericProjectID json.Number } // attributes describes the project's attributes keys. This is the internal // representation used to parse the json formatted output of metadata server. type attributes struct { CreatedBy string Hostname string BlockProjectKeys bool EnableCorePlugin *bool EnableOSLogin *bool EnableWindowsSSH *bool TwoFactor *bool SecurityKey *bool RequireCerts *bool SSHKeys []string WindowsKeys windowsKeys Diagnostics string DisableAddressManager *bool DisableAccountManager *bool EnableDiagnostics *bool EnableWSFC *bool WSFCAddresses string WSFCAgentPort string DisableTelemetry bool } // UnmarshalJSON unmarshals b into Attribute. func (a *attributes) UnmarshalJSON(b []byte) error { var mkbool = func(value bool) *bool { res := new(bool) *res = value return res } // Unmarshal to literal JSON types before doing anything else. type inner struct { CreatedBy string `json:"created-by"` BlockProjectKeys string `json:"block-project-ssh-keys"` Hostname string `json:"hostname"` Diagnostics string `json:"diagnostics"` DisableAccountManager string `json:"disable-account-manager"` DisableAddressManager string `json:"disable-address-manager"` EnableCorePlugin string `json:"enable-guest-agent-core-plugin"` EnableDiagnostics string `json:"enable-diagnostics"` EnableOSLogin string `json:"enable-oslogin"` EnableWindowsSSH string `json:"enable-windows-ssh"` EnableWSFC string `json:"enable-wsfc"` OldSSHKeys string `json:"sshKeys"` SSHKeys string `json:"ssh-keys"` TwoFactor string `json:"enable-oslogin-2fa"` SecurityKey string `json:"enable-oslogin-sk"` RequireCerts string `json:"enable-oslogin-certificates"` WindowsKeys windowsKeys `json:"windows-keys"` WSFCAddresses string `json:"wsfc-addrs"` WSFCAgentPort string `json:"wsfc-agent-port"` DisableTelemetry string `json:"disable-guest-telemetry"` } var temp inner if err := json.Unmarshal(b, &temp); err != nil { return err } a.Hostname = temp.Hostname a.Diagnostics = temp.Diagnostics a.WSFCAddresses = temp.WSFCAddresses a.WSFCAgentPort = temp.WSFCAgentPort a.WindowsKeys = temp.WindowsKeys a.CreatedBy = temp.CreatedBy if value, err := strconv.ParseBool(temp.BlockProjectKeys); err == nil { a.BlockProjectKeys = value } if value, err := strconv.ParseBool(temp.EnableCorePlugin); err == nil { a.EnableCorePlugin = mkbool(value) } if value, err := strconv.ParseBool(temp.EnableDiagnostics); err == nil { a.EnableDiagnostics = mkbool(value) } if value, err := strconv.ParseBool(temp.DisableAccountManager); err == nil { a.DisableAccountManager = mkbool(value) } if value, err := strconv.ParseBool(temp.DisableAddressManager); err == nil { a.DisableAddressManager = mkbool(value) } if value, err := strconv.ParseBool(temp.EnableOSLogin); err == nil { a.EnableOSLogin = mkbool(value) } if value, err := strconv.ParseBool(temp.EnableWindowsSSH); err == nil { a.EnableWindowsSSH = mkbool(value) } if value, err := strconv.ParseBool(temp.EnableWSFC); err == nil { a.EnableWSFC = mkbool(value) } if value, err := strconv.ParseBool(temp.TwoFactor); err == nil { a.TwoFactor = mkbool(value) } if value, err := strconv.ParseBool(temp.SecurityKey); err == nil { a.SecurityKey = mkbool(value) } if value, err := strconv.ParseBool(temp.RequireCerts); err == nil { a.RequireCerts = mkbool(value) } if value, err := strconv.ParseBool(temp.DisableTelemetry); err == nil { a.DisableTelemetry = value } // So SSHKeys will be nil instead of []string{} if temp.SSHKeys != "" { a.SSHKeys = strings.Split(temp.SSHKeys, "\n") } if temp.OldSSHKeys != "" { a.BlockProjectKeys = true a.SSHKeys = append(a.SSHKeys, strings.Split(temp.OldSSHKeys, "\n")...) } return nil } // windowsKey describes the WindowsKey metadata keys. This is the internal // representation used to parse the json formatted output of metadata server. type windowsKey struct { Email string ExpireOn string Exponent string Modulus string UserName string HashFunction string AddToAdministrators *bool PasswordLength int } // windowsKeys is a slice of WindowKey. type windowsKeys []windowsKey // UnmarshalJSON unmarshals b into WindowsKeys. func (k *windowsKeys) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } for _, jskey := range strings.Split(s, "\n") { var wk windowsKey if err := json.Unmarshal([]byte(jskey), &wk); err != nil { galog.Errorf("failed to unmarshal windows key from metadata: %s", err) continue } expired, err := ssh.CheckExpired(wk.ExpireOn) if err != nil { galog.Errorf("failed to check expiry for time %q: %s", wk.ExpireOn, err) continue } if wk.Exponent != "" && wk.Modulus != "" && wk.UserName != "" && !expired { *k = append(*k, wk) } } return nil } // UnmarshalJSON unmarshals b into WindowsKey. func (k *WindowsKey) UnmarshalJSON(b []byte) error { var key windowsKey if err := json.Unmarshal(b, &key); err != nil { return err } k.internal = &key return nil }