|
| 1 | +// Copyright 2023 Red Hat |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +package hyperv |
| 16 | + |
| 17 | +import ( |
| 18 | + "fmt" |
| 19 | + "os/exec" |
| 20 | + "path/filepath" |
| 21 | + |
| 22 | + "github.com/containers/libhvee/pkg/kvp" |
| 23 | + "github.com/coreos/ignition/v2/config/shared/errors" |
| 24 | + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" |
| 25 | + "github.com/coreos/ignition/v2/internal/distro" |
| 26 | + "github.com/coreos/ignition/v2/internal/platform" |
| 27 | + "github.com/coreos/ignition/v2/internal/providers/util" |
| 28 | + "github.com/coreos/ignition/v2/internal/resource" |
| 29 | + "github.com/coreos/vcontext/report" |
| 30 | +) |
| 31 | + |
| 32 | +const singleKey = "ignition.config" |
| 33 | + |
| 34 | +// Prefix for multiple config fragments to reassemble. The suffix is a |
| 35 | +// sequential integer starting from 0. |
| 36 | +const splitKeyPrefix = "ignition.config." |
| 37 | + |
| 38 | +func init() { |
| 39 | + platform.Register(platform.Provider{ |
| 40 | + Name: "hyperv", |
| 41 | + FetchWithFiles: fetchConfig, |
| 42 | + }) |
| 43 | +} |
| 44 | + |
| 45 | +func fetchConfig(f *resource.Fetcher) ([]types.File, types.Config, report.Report, error) { |
| 46 | + var kvpFiles []types.File |
| 47 | + |
| 48 | + // To read key-value pairs from the Windows host, the hv_util kernel |
| 49 | + // module must be loaded to create the kernel device |
| 50 | + _, err := f.Logger.LogCmd(exec.Command(distro.ModprobeCmd(), "hv_utils"), "loading hv_utils kernel module") |
| 51 | + if err != nil { |
| 52 | + return nil, types.Config{}, report.Report{}, fmt.Errorf("loading hv_utils kernel module: %w", err) |
| 53 | + } |
| 54 | + |
| 55 | + keyValuePairs, err := kvp.GetKeyValuePairs() |
| 56 | + if err != nil { |
| 57 | + return nil, types.Config{}, report.Report{}, fmt.Errorf("reading key-value pairs: %w", err) |
| 58 | + } |
| 59 | + |
| 60 | + var ign string |
| 61 | + kv, err := keyValuePairs[kvp.DefaultKVPPoolID].GetValueByKey(singleKey) |
| 62 | + if err == nil { |
| 63 | + f.Logger.Debug("found single KVP key") |
| 64 | + ign = kv.Value |
| 65 | + } else if err != kvp.ErrKeyNotFound { |
| 66 | + return nil, types.Config{}, report.Report{}, fmt.Errorf("looking up single KVP key: %w", err) |
| 67 | + } |
| 68 | + |
| 69 | + if ign == "" { |
| 70 | + ign, err = keyValuePairs.GetSplitKeyValues(splitKeyPrefix, kvp.DefaultKVPPoolID) |
| 71 | + if err == nil { |
| 72 | + f.Logger.Debug("found concatenated KVP keys") |
| 73 | + } else if err != kvp.ErrNoKeyValuePairsFound { |
| 74 | + return nil, types.Config{}, report.Report{}, fmt.Errorf("reassembling split config: %w", err) |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + // hv_kvp_daemon writes pools to the filesystem in /var/lib/hyperv. |
| 79 | + // We've already read the pool data, and the host won't send it again |
| 80 | + // on this boot, so we need to write the files ourselves. |
| 81 | + for poolID := range keyValuePairs { |
| 82 | + // hv_kvp_daemon writes the pool files with mode 644 in a |
| 83 | + // directory with mode 755. This isn't safe for us, since |
| 84 | + // it leaks the config to non-root users, including on |
| 85 | + // subsequent boots. |
| 86 | + // - There's no API that lets us delete the KVPs from the host. |
| 87 | + // - We could filter out the KVPs when writing the pools, |
| 88 | + // but if hv_kvp_daemon runs on subsequent boots, it could |
| 89 | + // re-add them. |
| 90 | + // - The caller doesn't give us a way to create directory |
| 91 | + // entries, only files; and we probably shouldn't set |
| 92 | + // restrictive permissions on /var/lib/hyperv because it |
| 93 | + // hypothetically might be used for other purposes. |
| 94 | + // Avoid the issue by setting the files to mode 600. |
| 95 | + // hv_kvp_daemon won't change the mode afterward. |
| 96 | + poolPath := filepath.Join(kvp.DefaultKVPFilePath, fmt.Sprintf("%s%d", kvp.DefaultKVPBaseName, poolID)) |
| 97 | + kvpFiles = append(kvpFiles, util.MakeProviderOutputFile(poolPath, 0600, keyValuePairs.EncodePoolFile(poolID))) |
| 98 | + } |
| 99 | + |
| 100 | + if ign == "" { |
| 101 | + return kvpFiles, types.Config{}, report.Report{}, errors.ErrEmpty |
| 102 | + } |
| 103 | + |
| 104 | + c, r, err := util.ParseConfig(f.Logger, []byte(ign)) |
| 105 | + return kvpFiles, c, r, err |
| 106 | +} |
0 commit comments