podman

Форк
0
251 строка · 7.0 Кб
1
package mkcw
2

3
import (
4
	"bufio"
5
	"bytes"
6
	"encoding/json"
7
	"errors"
8
	"fmt"
9
	"net/http"
10
	"net/url"
11
	"os"
12
	"os/exec"
13
	"path"
14
	"path/filepath"
15
	"strings"
16

17
	"github.com/containers/buildah/internal/mkcw/types"
18
	"github.com/containers/storage/pkg/fileutils"
19
	"github.com/sirupsen/logrus"
20
)
21

22
type (
23
	RegistrationRequest = types.RegistrationRequest
24
	TeeConfig           = types.TeeConfig
25
	TeeConfigFlags      = types.TeeConfigFlags
26
	TeeConfigMinFW      = types.TeeConfigMinFW
27
)
28

29
type measurementError struct {
30
	err error
31
}
32

33
func (m measurementError) Error() string {
34
	return fmt.Sprintf("generating measurement for attestation: %v", m.err)
35
}
36

37
type attestationError struct {
38
	err error
39
}
40

41
func (a attestationError) Error() string {
42
	return fmt.Sprintf("registering workload: %v", a.err)
43
}
44

45
type httpError struct {
46
	statusCode int
47
}
48

49
func (h httpError) Error() string {
50
	if statusText := http.StatusText(h.statusCode); statusText != "" {
51
		return fmt.Sprintf("received server status %d (%q)", h.statusCode, statusText)
52
	}
53
	return fmt.Sprintf("received server status %d", h.statusCode)
54
}
55

56
// SendRegistrationRequest registers a workload with the specified decryption
57
// passphrase with the service whose location is part of the WorkloadConfig.
58
func SendRegistrationRequest(workloadConfig WorkloadConfig, diskEncryptionPassphrase, firmwareLibrary string, ignoreAttestationErrors bool, logger *logrus.Logger) error {
59
	if workloadConfig.AttestationURL == "" {
60
		return errors.New("attestation URL not provided")
61
	}
62

63
	// Measure the execution environment.
64
	measurement, err := GenerateMeasurement(workloadConfig, firmwareLibrary)
65
	if err != nil {
66
		if !ignoreAttestationErrors {
67
			return &measurementError{err}
68
		}
69
		logger.Warnf("generating measurement for attestation: %v", err)
70
	}
71

72
	// Build the workload registration (attestation) request body.
73
	var teeConfigBytes []byte
74
	switch workloadConfig.Type {
75
	case SEV, SEV_NO_ES, SNP:
76
		var cbits types.TeeConfigFlagBits
77
		switch workloadConfig.Type {
78
		case SEV:
79
			cbits = types.SEV_CONFIG_NO_DEBUG |
80
				types.SEV_CONFIG_NO_KEY_SHARING |
81
				types.SEV_CONFIG_ENCRYPTED_STATE |
82
				types.SEV_CONFIG_NO_SEND |
83
				types.SEV_CONFIG_DOMAIN |
84
				types.SEV_CONFIG_SEV
85
		case SEV_NO_ES:
86
			cbits = types.SEV_CONFIG_NO_DEBUG |
87
				types.SEV_CONFIG_NO_KEY_SHARING |
88
				types.SEV_CONFIG_NO_SEND |
89
				types.SEV_CONFIG_DOMAIN |
90
				types.SEV_CONFIG_SEV
91
		case SNP:
92
			cbits = types.SNP_CONFIG_SMT |
93
				types.SNP_CONFIG_MANDATORY |
94
				types.SNP_CONFIG_MIGRATE_MA |
95
				types.SNP_CONFIG_DEBUG
96
		default:
97
			panic("internal error") // shouldn't happen
98
		}
99
		teeConfig := TeeConfig{
100
			Flags: TeeConfigFlags{
101
				Bits: cbits,
102
			},
103
			MinFW: TeeConfigMinFW{
104
				Major: 0,
105
				Minor: 0,
106
			},
107
		}
108
		teeConfigBytes, err = json.Marshal(teeConfig)
109
		if err != nil {
110
			return err
111
		}
112
	default:
113
		return fmt.Errorf("don't know how to generate tee_config for %q TEEs", workloadConfig.Type)
114
	}
115

116
	registrationRequest := RegistrationRequest{
117
		WorkloadID:        workloadConfig.WorkloadID,
118
		LaunchMeasurement: measurement,
119
		TeeConfig:         string(teeConfigBytes),
120
		Passphrase:        diskEncryptionPassphrase,
121
	}
122
	registrationRequestBytes, err := json.Marshal(registrationRequest)
123
	if err != nil {
124
		return err
125
	}
126

127
	// Register the workload.
128
	parsedURL, err := url.Parse(workloadConfig.AttestationURL)
129
	if err != nil {
130
		return err
131
	}
132
	parsedURL.Path = path.Join(parsedURL.Path, "/kbs/v0/register_workload")
133
	if err != nil {
134
		return err
135
	}
136
	url := parsedURL.String()
137
	requestContentType := "application/json"
138
	requestBody := bytes.NewReader(registrationRequestBytes)
139
	defer http.DefaultClient.CloseIdleConnections()
140
	resp, err := http.Post(url, requestContentType, requestBody)
141
	if resp != nil {
142
		if resp.Body != nil {
143
			resp.Body.Close()
144
		}
145
		switch resp.StatusCode {
146
		default:
147
			if !ignoreAttestationErrors {
148
				return &attestationError{&httpError{resp.StatusCode}}
149
			}
150
			logger.Warn(attestationError{&httpError{resp.StatusCode}}.Error())
151
		case http.StatusOK, http.StatusAccepted:
152
			// great!
153
		}
154
	}
155
	if err != nil {
156
		if !ignoreAttestationErrors {
157
			return &attestationError{err}
158
		}
159
		logger.Warn(attestationError{err}.Error())
160
	}
161
	return nil
162
}
163

164
// GenerateMeasurement generates the runtime measurement using the CPU count,
165
// memory size, and the firmware shared library, whatever it's called, wherever
166
// it is.
167
// If firmwareLibrary is a path, it will be the only one checked.
168
// If firmwareLibrary is a filename, it will be checked for in a hard-coded set
169
// of directories.
170
// If firmwareLibrary is empty, both the filename and the directory it is in
171
// will be taken from a hard-coded set of candidates.
172
func GenerateMeasurement(workloadConfig WorkloadConfig, firmwareLibrary string) (string, error) {
173
	cpuString := fmt.Sprintf("%d", workloadConfig.CPUs)
174
	memoryString := fmt.Sprintf("%d", workloadConfig.Memory)
175
	var prefix string
176
	switch workloadConfig.Type {
177
	case SEV:
178
		prefix = "SEV-ES"
179
	case SEV_NO_ES:
180
		prefix = "SEV"
181
	case SNP:
182
		prefix = "SNP"
183
	default:
184
		return "", fmt.Errorf("don't know which measurement to use for TEE type %q", workloadConfig.Type)
185
	}
186

187
	sharedLibraryDirs := []string{
188
		"/usr/local/lib64",
189
		"/usr/local/lib",
190
		"/lib64",
191
		"/lib",
192
		"/usr/lib64",
193
		"/usr/lib",
194
	}
195
	if llp, ok := os.LookupEnv("LD_LIBRARY_PATH"); ok {
196
		sharedLibraryDirs = append(sharedLibraryDirs, strings.Split(llp, ":")...)
197
	}
198
	libkrunfwNames := []string{
199
		"libkrunfw-sev.so.4",
200
		"libkrunfw-sev.so.3",
201
		"libkrunfw-sev.so",
202
	}
203
	var pathsToCheck []string
204
	if firmwareLibrary == "" {
205
		for _, sharedLibraryDir := range sharedLibraryDirs {
206
			if sharedLibraryDir == "" {
207
				continue
208
			}
209
			for _, libkrunfw := range libkrunfwNames {
210
				candidate := filepath.Join(sharedLibraryDir, libkrunfw)
211
				pathsToCheck = append(pathsToCheck, candidate)
212
			}
213
		}
214
	} else {
215
		if filepath.IsAbs(firmwareLibrary) {
216
			pathsToCheck = append(pathsToCheck, firmwareLibrary)
217
		} else {
218
			for _, sharedLibraryDir := range sharedLibraryDirs {
219
				if sharedLibraryDir == "" {
220
					continue
221
				}
222
				candidate := filepath.Join(sharedLibraryDir, firmwareLibrary)
223
				pathsToCheck = append(pathsToCheck, candidate)
224
			}
225
		}
226
	}
227
	for _, candidate := range pathsToCheck {
228
		if err := fileutils.Lexists(candidate); err == nil {
229
			var stdout, stderr bytes.Buffer
230
			logrus.Debugf("krunfw_measurement -c %s -m %s %s", cpuString, memoryString, candidate)
231
			cmd := exec.Command("krunfw_measurement", "-c", cpuString, "-m", memoryString, candidate)
232
			cmd.Stdout = &stdout
233
			cmd.Stderr = &stderr
234
			if err := cmd.Run(); err != nil {
235
				if stderr.Len() > 0 {
236
					err = fmt.Errorf("krunfw_measurement: %s: %w", strings.TrimSpace(stderr.String()), err)
237
				}
238
				return "", err
239
			}
240
			scanner := bufio.NewScanner(&stdout)
241
			for scanner.Scan() {
242
				line := scanner.Text()
243
				if strings.HasPrefix(line, prefix+":") {
244
					return strings.TrimSpace(strings.TrimPrefix(line, prefix+":")), nil
245
				}
246
			}
247
			return "", fmt.Errorf("generating measurement: no line starting with %q found in output from krunfw_measurement", prefix+":")
248
		}
249
	}
250
	return "", fmt.Errorf("generating measurement: none of %v found: %w", pathsToCheck, os.ErrNotExist)
251
}
252

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.