diff --git a/charts/README.md b/charts/README.md index e34474615..c16901629 100644 --- a/charts/README.md +++ b/charts/README.md @@ -62,8 +62,9 @@ their default values. | `marbleInjector.namespaceSelector` | object | NamespaceSelector to trigger marble-injector mutation, See the [K8S documentation](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector) for more information | `{}` | | `nodeSelector` | object | NodeSelector section, See the [K8S documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) for more information | `{"beta.kubernetes.io/os": "linux"}` | | `tolerations` | object | Tolerations section, See the [K8S documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for more information | `{key:"sgx.intel.com/epc",operator:"Exists",effect:"NoSchedule"}` | -| `dcap.pccsUrl` | string | URL of the PCCS | `"https://global.acccache.azure.net/sgx/certification/v4/"` | -| `dcap.useSecureCert` | string | Whether or not the TLS certificate of the PCCS should be verified | `"TRUE"` | +| `dcap.qcnlConfig` | string | Inline defined QCNL configuration. If set, this configuration is used instead of the one created from `dcap.pccsUrl` and `dcap.useSecureCert` | `""` | +| `dcap.pccsUrl` | string | URL of the PCCS. Only used if `dcap.qcnlConfig` is not set | `"https://global.acccache.azure.net/sgx/certification/v4/"` | +| `dcap.useSecureCert` | string | Whether or not the TLS certificate of the PCCS should be verified. Only used if `dcap.qcnlConfig` is not set | `true` | ## Add new version (maintainers) diff --git a/charts/templates/sgx_qcnl.yaml b/charts/templates/sgx_qcnl.yaml index f4941b3a6..b328515c2 100644 --- a/charts/templates/sgx_qcnl.yaml +++ b/charts/templates/sgx_qcnl.yaml @@ -13,7 +13,18 @@ metadata: {{ .Values.global.coordinatorComponentLabel }}: dcap-config {{ .Values.global.coordinatorNamespaceLabel }}: {{ .Release.Namespace }} data: + {{- if .Values.dcap.qcnlConfig }} + sgx_default_qcnl.conf: |- +{{ .Values.dcap.qcnlConfig | indent 4 }} + {{- else }} sgx_default_qcnl.conf: | - PCCS_URL={{ .Values.dcap.pccsUrl }} - USE_SECURE_CERT={{ .Values.dcap.useSecureCert }} + { + "pccs_url": "{{ .Values.dcap.pccsUrl }}", + {{- if kindIs "bool" .Values.dcap.useSecureCert }} + "use_secure_cert": {{ .Values.dcap.useSecureCert }} + {{- else }} + "use_secure_cert": {{ eq (lower (toString .Values.dcap.useSecureCert)) "true" }} + {{- end }} + } + {{- end}} {{ end }} diff --git a/charts/values.yaml b/charts/values.yaml index c8a238423..25f6e63e7 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -125,5 +125,8 @@ nodeSelector: # DCAP configuration settings dcap: + # Set to use an inline defined QCNL configuration + qcnlConfig: "" + # If qcnlConfig is not set, the default values below are used to create a config file pccsUrl: "https://global.acccache.azure.net/sgx/certification/v4/" - useSecureCert: "TRUE" + useSecureCert: true diff --git a/cli/internal/cmd/install.go b/cli/internal/cmd/install.go index bfe273a59..1a819729d 100644 --- a/cli/internal/cmd/install.go +++ b/cli/internal/cmd/install.go @@ -45,8 +45,9 @@ marblerun install --dcap-pccs-url https://pccs.example.com/sgx/certification/v4/ cmd.Flags().String("version", "", "Version of the Coordinator to install, latest by default") cmd.Flags().String("resource-key", "", "Resource providing SGX, different depending on used device plugin. Use this to set tolerations/resources if your device plugin is not supported by MarbleRun") cmd.Flags().String("dcap-qpl", "azure", `Quote provider library to use by the Coordinator. One of {"azure", "intel"}`) - cmd.Flags().String("dcap-pccs-url", "https://global.acccache.azure.net/sgx/certification/v4/", "Provisioning Certificate Caching Service (PCCS) server address. Defaults to Azure PCCS.") - cmd.Flags().String("dcap-secure-cert", "TRUE", "To accept insecure HTTPS certificate from the PCCS, set this option to FALSE") + cmd.Flags().String("dcap-qcnl-config-file", "", "Path to a custom QCNL configuration file. Mutually exclusive with \"--dcap-pccs-url\" and \"--dcap-secure-cert\".") + cmd.Flags().String("dcap-pccs-url", "https://global.acccache.azure.net/sgx/certification/v4/", "Provisioning Certificate Caching Service (PCCS) server address. Defaults to Azure PCCS. Mutually exclusive with \"--dcap-qcnl-config-file\"") + cmd.Flags().String("dcap-secure-cert", "TRUE", "To accept insecure HTTPS certificate from the PCCS, set this option to FALSE. Mutually exclusive with \"--dcap-qcnl-config-file\"") cmd.Flags().String("enterprise-access-token", "", "Access token for Enterprise Coordinator. Leave empty for default installation") cmd.Flags().Bool("simulation", false, "Set MarbleRun to start in simulation mode") cmd.Flags().Bool("disable-auto-injection", false, "Install MarbleRun without auto-injection webhook") @@ -54,6 +55,9 @@ marblerun install --dcap-pccs-url https://pccs.example.com/sgx/certification/v4/ cmd.Flags().Int("mesh-server-port", 2001, "Set the mesh server port. Needs to be configured to the same port as in the data-plane marbles") cmd.Flags().Int("client-server-port", 4433, "Set the client server port. Needs to be configured to the same port as in your client tool stack") + cmd.MarkFlagsMutuallyExclusive("dcap-qcnl-config-file", "dcap-pccs-url") + cmd.MarkFlagsMutuallyExclusive("dcap-qcnl-config-file", "dcap-secure-cert") + must(cmd.Flags().MarkDeprecated("dcap-qpl", "All platforms use the same QPL now. Use --dcap-pccs-url to configure the PCCS server address.")) return cmd @@ -116,6 +120,7 @@ func cliInstall(cmd *cobra.Command, helmClient *helm.Client, kubeClient kubernet values, err := helm.UpdateValues( helm.Options{ Hostname: flags.hostname, + QCNLConfigFile: flags.qcnlConfigFile, PCCSURL: flags.pccsURL, UseSecureCert: flags.useSecureCert, AccessToken: flags.accessToken, @@ -275,6 +280,7 @@ type installFlags struct { hostname []string version string resourceKey string + qcnlConfigFile string pccsURL string useSecureCert string accessToken string @@ -302,6 +308,10 @@ func parseInstallFlags(cmd *cobra.Command) (installFlags, error) { if err != nil { return installFlags{}, err } + qcnlConfigFile, err := cmd.Flags().GetString("dcap-qcnl-config-file") + if err != nil { + return installFlags{}, err + } pccsURL, err := cmd.Flags().GetString("dcap-pccs-url") if err != nil { return installFlags{}, err @@ -344,6 +354,7 @@ func parseInstallFlags(cmd *cobra.Command) (installFlags, error) { hostname: hostname, version: version, resourceKey: resourceKey, + qcnlConfigFile: qcnlConfigFile, pccsURL: pccsURL, useSecureCert: useSecureCert, accessToken: accessToken, diff --git a/cli/internal/helm/client.go b/cli/internal/helm/client.go index 15a40e107..bbc2d2a9e 100644 --- a/cli/internal/helm/client.go +++ b/cli/internal/helm/client.go @@ -32,6 +32,7 @@ import ( // Options contains the values to set in the helm chart. type Options struct { Hostname []string + QCNLConfigFile string PCCSURL string UseSecureCert string AccessToken string @@ -92,12 +93,13 @@ func (c *Client) GetChart(chartPath, version string) (*chart.Chart, error) { } // UpdateValues merges the provided options with the default values of the chart. -func UpdateValues(options Options, chartValues map[string]interface{}) (map[string]interface{}, error) { +func UpdateValues(options Options, chartValues map[string]any) (map[string]any, error) { + fileValues := []string{} stringValues := []string{} stringValues = append(stringValues, fmt.Sprintf("coordinator.meshServerPort=%d", options.CoordinatorGRPCPort)) stringValues = append(stringValues, fmt.Sprintf("coordinator.clientServerPort=%d", options.CoordinatorRESTPort)) - if coordinatorOpts, ok := chartValues["coordinator"].(map[string]interface{}); ok { + if coordinatorOpts, ok := chartValues["coordinator"].(map[string]any); ok { if existingHostname, ok := coordinatorOpts["hostname"].(string); ok && existingHostname != "" { options.Hostname = append(options.Hostname, existingHostname) } @@ -113,32 +115,36 @@ func UpdateValues(options Options, chartValues map[string]interface{}) (map[stri fmt.Sprintf("dcap=%s", "null"), ) } else { - stringValues = append(stringValues, - fmt.Sprintf("dcap.pccsUrl=%s", options.PCCSURL), - fmt.Sprintf("dcap.useSecureCert=%s", options.UseSecureCert), - ) + if options.QCNLConfigFile != "" { + fileValues = append(fileValues, fmt.Sprintf("dcap.qcnlConfig=%s", options.QCNLConfigFile)) + } else { + stringValues = append(stringValues, + fmt.Sprintf("dcap.pccsUrl=%s", options.PCCSURL), + fmt.Sprintf("dcap.useSecureCert=%s", options.UseSecureCert), + ) + } // Helms value merge function will overwrite any preset values for "tolerations" if we set new ones here // To avoid this we set the new toleration for "resourceKey" and copy all preset tolerations needToleration := true idx := 0 - for _, toleration := range chartValues["tolerations"].([]interface{}) { - if key, ok := toleration.(map[string]interface{})["key"]; ok { + for _, toleration := range chartValues["tolerations"].([]any) { + if key, ok := toleration.(map[string]any)["key"]; ok { if key == options.SGXResourceKey { needToleration = false } stringValues = append(stringValues, fmt.Sprintf("tolerations[%d].key=%v", idx, key)) } - if operator, ok := toleration.(map[string]interface{})["operator"]; ok { + if operator, ok := toleration.(map[string]any)["operator"]; ok { stringValues = append(stringValues, fmt.Sprintf("tolerations[%d].operator=%v", idx, operator)) } - if effect, ok := toleration.(map[string]interface{})["effect"]; ok { + if effect, ok := toleration.(map[string]any)["effect"]; ok { stringValues = append(stringValues, fmt.Sprintf("tolerations[%d].effect=%v", idx, effect)) } - if value, ok := toleration.(map[string]interface{})["value"]; ok { + if value, ok := toleration.(map[string]any)["value"]; ok { stringValues = append(stringValues, fmt.Sprintf("tolerations[%d].value=%v", idx, value)) } - if tolerationSeconds, ok := toleration.(map[string]interface{})["tolerationSeconds"]; ok { + if tolerationSeconds, ok := toleration.(map[string]any)["tolerationSeconds"]; ok { stringValues = append(stringValues, fmt.Sprintf("tolerations[%d].tolerationSeconds=%v", idx, tolerationSeconds)) } idx++ @@ -154,7 +160,7 @@ func UpdateValues(options Options, chartValues map[string]interface{}) (map[stri // Configure enterprise access token if options.AccessToken != "" { - coordinatorCfg, ok := chartValues["coordinator"].(map[string]interface{}) + coordinatorCfg, ok := chartValues["coordinator"].(map[string]any) if !ok { return nil, errors.New("coordinator not found in chart values") } @@ -179,12 +185,24 @@ func UpdateValues(options Options, chartValues map[string]interface{}) (map[stri stringValues = append(stringValues, fmt.Sprintf("marbleInjector.resourceKey=%s", options.SGXResourceKey)) } - finalValues := map[string]interface{}{} + finalValues := map[string]any{} for _, val := range stringValues { if err := strvals.ParseInto(val, finalValues); err != nil { return nil, fmt.Errorf("parsing value %q into final values: %w", val, err) } } + for _, val := range fileValues { + runeReader := func(rs []rune) (any, error) { + file, err := os.ReadFile(string(rs)) + if err != nil { + return nil, fmt.Errorf("reading file %q: %w", string(rs), err) + } + return string(file), nil + } + if err := strvals.ParseIntoFile(val, finalValues, runeReader); err != nil { + return nil, fmt.Errorf("parsing file value %q into final values: %w", val, err) + } + } if !options.SimulationMode { setSGXValues(options.SGXResourceKey, finalValues, chartValues) @@ -194,7 +212,7 @@ func UpdateValues(options Options, chartValues map[string]interface{}) (map[stri } // Install installs MarbleRun using the provided chart and values. -func (c *Client) Install(ctx context.Context, wait bool, chart *chart.Chart, values map[string]interface{}) error { +func (c *Client) Install(ctx context.Context, wait bool, chart *chart.Chart, values map[string]any) error { installer := action.NewInstall(c.config) installer.Namespace = c.namespace installer.ReleaseName = release @@ -277,31 +295,31 @@ func (c *Client) getRepo(name string, url string) error { // setSGXValues sets the needed values for the coordinator as a map[string]interface. // strvals can't parse keys which include dots, e.g. setting as a resource limit key "sgx.intel.com/epc" will lead to errors. -func setSGXValues(resourceKey string, values, chartValues map[string]interface{}) { - values["coordinator"].(map[string]interface{})["resources"] = map[string]interface{}{ - "limits": map[string]interface{}{}, - "requests": map[string]interface{}{}, +func setSGXValues(resourceKey string, values, chartValues map[string]any) { + values["coordinator"].(map[string]any)["resources"] = map[string]any{ + "limits": map[string]any{}, + "requests": map[string]any{}, } var needNewLimit bool limit := k8sutil.GetEPCResourceLimit(resourceKey) // remove all previously set sgx resource limits - if presetLimits, ok := chartValues["coordinator"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{}); ok { + if presetLimits, ok := chartValues["coordinator"].(map[string]any)["resources"].(map[string]any)["limits"].(map[string]any); ok { for oldResourceKey := range presetLimits { // Make sure the key we delete is an unwanted sgx resource and not a custom resource or common resource (cpu, memory, etc.) if needsDeletion(oldResourceKey, resourceKey) { - values["coordinator"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{})[oldResourceKey] = nil + values["coordinator"].(map[string]any)["resources"].(map[string]any)["limits"].(map[string]any)[oldResourceKey] = nil needNewLimit = true } } } // remove all previously set sgx resource requests - if presetLimits, ok := chartValues["coordinator"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{}); ok { + if presetLimits, ok := chartValues["coordinator"].(map[string]any)["resources"].(map[string]any)["requests"].(map[string]any); ok { for oldResourceKey := range presetLimits { if needsDeletion(oldResourceKey, resourceKey) { - values["coordinator"].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})[oldResourceKey] = nil + values["coordinator"].(map[string]any)["resources"].(map[string]any)["requests"].(map[string]any)[oldResourceKey] = nil needNewLimit = true } } @@ -309,13 +327,13 @@ func setSGXValues(resourceKey string, values, chartValues map[string]interface{} // Set the new sgx resource limit, kubernetes will automatically set a resource request equal to the limit if needNewLimit { - values["coordinator"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{})[resourceKey] = limit + values["coordinator"].(map[string]any)["resources"].(map[string]any)["limits"].(map[string]any)[resourceKey] = limit } // Make sure provision and enclave bit is set if the Intel plugin is used if resourceKey == k8sutil.IntelEpc.String() { - values["coordinator"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{})[k8sutil.IntelProvision.String()] = 1 - values["coordinator"].(map[string]interface{})["resources"].(map[string]interface{})["limits"].(map[string]interface{})[k8sutil.IntelEnclave.String()] = 1 + values["coordinator"].(map[string]any)["resources"].(map[string]any)["limits"].(map[string]any)[k8sutil.IntelProvision.String()] = 1 + values["coordinator"].(map[string]any)["resources"].(map[string]any)["limits"].(map[string]any)[k8sutil.IntelEnclave.String()] = 1 } } @@ -351,4 +369,4 @@ func keyInList(key string, list []string) bool { return false } -func nopLog(_ string, _ ...interface{}) {} +func nopLog(_ string, _ ...any) {} diff --git a/docs/docs/reference/cli.md b/docs/docs/reference/cli.md index 75184e226..e90e971f9 100644 --- a/docs/docs/reference/cli.md +++ b/docs/docs/reference/cli.md @@ -64,8 +64,9 @@ marblerun install --dcap-pccs-url https://pccs.example.com/sgx/certification/v4/ ``` --client-server-port int Set the client server port. Needs to be configured to the same port as in your client tool stack (default 4433) - --dcap-pccs-url string Provisioning Certificate Caching Service (PCCS) server address. Defaults to Azure PCCS. (default "https://global.acccache.azure.net/sgx/certification/v4/") - --dcap-secure-cert string To accept insecure HTTPS certificate from the PCCS, set this option to FALSE (default "TRUE") + --dcap-pccs-url string Provisioning Certificate Caching Service (PCCS) server address. Defaults to Azure PCCS. Mutually exclusive with "--dcap-qcnl-config-file" (default "https://global.acccache.azure.net/sgx/certification/v4/") + --dcap-qcnl-config-file string Path to a custom QCNL configuration file. Mutually exclusive with "--dcap-pccs-url" and "--dcap-secure-cert". + --dcap-secure-cert string To accept insecure HTTPS certificate from the PCCS, set this option to FALSE. Mutually exclusive with "--dcap-qcnl-config-file" (default "TRUE") --disable-auto-injection Install MarbleRun without auto-injection webhook --domain strings Sets additional DNS names and IPs for the Coordinator TLS certificate --enterprise-access-token string Access token for Enterprise Coordinator. Leave empty for default installation diff --git a/util/tls.go b/util/tls.go index 225d04f4e..ba58c4948 100644 --- a/util/tls.go +++ b/util/tls.go @@ -38,7 +38,7 @@ func MustGenerateTestMarbleCredentials() (cert *x509.Certificate, csrRaw []byte, panic(err) } csrRaw = csr.Raw - return + return cert, csrRaw, privk } // GenerateCert generates a new self-signed certificate associated key-pair.