AB#2306 Public image sharing in Google (#358)

* document how to publicly share images in gcloud
* Write disclamer in debugd
* Add disclamer about debug images to contributing file
* Print debug banner on startup
Signed-off-by: Fabian Kammel <fk@edgeless.systems>
This commit is contained in:
Fabian Kammel 2022-08-16 15:53:54 +02:00 committed by GitHub
parent abb4fb4f0f
commit 170a8bf5e0
10 changed files with 118 additions and 16 deletions

View File

@ -23,7 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Kubernetes operator for Constellation nodes with ability to update node images.
- cilium strict pod2pod encryption.
- CoreOS images are publicly available for GCP.
- Cilium strict pod2pod encryption.
- Add a configurable list of enforced measurements to the config. If an expected measurement can not be verified during attestation, but it is not in the list of enforced measurements, only a warning is logged.
### Changed

View File

@ -10,6 +10,14 @@ ctest
[Run CI e2e tests](/.github/docs/README.md)
### Debug Images
> :warning: These images are not safe to use in production environments. :warning:
As described in [debugd](/debugd/README.md), it is possible to use a CoreOS image targeted at dev environments. This image allows to upload any [bootstrapper](/bootstrapper/README.md) using [cdbg](/debugd/cdbg).
To enable the upload, an additional **unsecured** port (4000) is opened which accepts any binary to be run on target machine. **Make sure that this machine is not exposed to the internet.**
## Linting
This projects uses [golangci-lint](https://golangci-lint.run/) for linting.

View File

@ -50,6 +50,10 @@ func configFetchMeasurements(cmd *cobra.Command, fileHandler file.Handler, clien
return err
}
if conf.IsImageDebug() {
cmd.Println("Configured image does not look like a released production image. Double check image before deploying to production.")
}
if err := flags.updateURLs(conf); err != nil {
return err
}

View File

@ -73,6 +73,10 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
return fmt.Errorf("reading and validating config: %w", err)
}
if config.IsImageDebug() {
cmd.Println("Configured image does not look like a released production image. Double check image before deploying to production.")
}
if !flags.yes {
// Ask user to confirm action.
cmd.Printf("The following Constellation cluster will be created:\n")

View File

@ -66,8 +66,8 @@ func deploy(cmd *cobra.Command, fileHandler file.Handler, constellationConfig *c
debugConfig.ConstellationDebugConfig.BootstrapperPath = overrideBootstrapperPath
}
if !state.ImageNameContainsDebug(constellationConfig) {
log.Println("WARN: constellation image does not contain 'debug', are you using a debug image?")
if !constellationConfig.IsImageDebug() {
log.Println("WARN: constellation image does not look like a debug image. Are you using a debug image?")
}
overrideIPs, err := cmd.Flags().GetStringSlice("ips")

View File

@ -2,7 +2,6 @@ package state
import (
"errors"
"strings"
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
"github.com/edgelesssys/constellation/internal/config"
@ -77,15 +76,3 @@ func getQEMUInstances(stat state.ConstellationState, _ *config.Config) (controlP
workers = cloudtypes.ScalingGroup{Instances: stat.QEMUWorkers}
return
}
// ImageNameContainsDebug check wether the image name in config contains "debug".
func ImageNameContainsDebug(config *config.Config) bool {
switch {
case config.Provider.GCP != nil:
return strings.Contains(config.Provider.GCP.Image, "debug")
case config.Provider.Azure != nil:
return strings.Contains(config.Provider.Azure.Image, "debug")
default:
return false
}
}

View File

@ -2,6 +2,7 @@ package main
import (
"flag"
"fmt"
"net"
"os"
"sync"
@ -20,6 +21,13 @@ import (
"golang.org/x/net/context"
)
const debugBanner = `
**************************************
THIS A CONSTELLATION DEBUG IMAGE.
DO NOT USE IN PRODUCTION.
**************************************
`
func main() {
wg := &sync.WaitGroup{}
verbosity := flag.Int("v", 0, logger.CmdLineVerbosityDescription)
@ -62,6 +70,8 @@ func main() {
panic(err)
}
writeDebugBanner(log)
wg.Add(1)
go sched.Start(ctx, wg)
wg.Add(1)
@ -69,3 +79,15 @@ func main() {
wg.Wait()
}
func writeDebugBanner(log *logger.Logger) {
tty, err := os.OpenFile("/dev/ttyS0", os.O_WRONLY, os.ModeAppend)
if err != nil {
log.Infof("Unable to open /dev/ttyS0 for printing banner: %v", err)
return
}
defer tty.Close()
if _, err := fmt.Fprint(tty, debugBanner); err != nil {
log.Infof("Unable to print to /dev/ttyS0: %v", err)
}
}

View File

@ -122,6 +122,10 @@ upload-gcp: $(GCP_IMAGE_PATH)
--guest-os-features=GVNIC,SEV_CAPABLE,VIRTIO_SCSI_MULTIQUEUE,UEFI_COMPATIBLE \
--labels=bootstrapper-sha1=$$(shasum $(BOOTSTRAPPER_OVERRIDE_PATH) | cut -d " " -f 1),bootstrapper-sha512=$$(sha512sum $(BOOTSTRAPPER_OVERRIDE_PATH) | cut -d " " -f 1 | cut -c-63) \
--project $(GCP_PROJECT)
gcloud compute images add-iam-policy-binding $(GCP_IMAGE_NAME) \
--project $(GCP_PROJECT) \
--member='allAuthenticatedUsers' \
--role='roles/compute.imageUser'
gsutil rm gs://$(GCP_BUCKET)/$(GCP_IMAGE_FILENAME)
image-azure: $(AZURE_IMAGE_PATH)

View File

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"io/fs"
"regexp"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/constants"
@ -346,6 +347,22 @@ func (c *Config) RemoveProviderExcept(provider cloudprovider.Provider) {
}
}
// IsImageDebug checks whether image name looks like a release image, if not it is
// probably a debug image. In the end we do not if bootstrapper or debugd
// was put inside an image just by looking at its name.
func (c *Config) IsImageDebug() bool {
switch {
case c.Provider.GCP != nil:
gcpRegex := regexp.MustCompile(`^projects\/constellation-images\/global\/images\/constellation-v[\d]+-[\d]+-[\d]+$`)
return !gcpRegex.MatchString(c.Provider.GCP.Image)
case c.Provider.Azure != nil:
azureRegex := regexp.MustCompile(`^\/subscriptions\/0d202bbb-4fa7-4af8-8125-58c269a05435\/resourceGroups\/constellation-images\/providers\/Microsoft.Compute\/galleries\/Constellation\/images\/constellation\/versions\/[\d]+.[\d]+.[\d]+$`)
return !azureRegex.MatchString(c.Provider.Azure.Image)
default:
return false
}
}
// FromFile returns config file with `name` read from `fileHandler` by parsing
// it as YAML.
func FromFile(fileHandler file.Handler, name string) (*Config, error) {

View File

@ -321,3 +321,58 @@ func TestConfig_UpdateMeasurements(t *testing.T) {
assert.Equal(newMeasurements, conf.Provider.QEMU.Measurements)
}
}
func TestConfig_IsImageDebug(t *testing.T) {
testCases := map[string]struct {
conf *Config
want bool
}{
"gcp release": {
conf: func() *Config {
conf := Default()
conf.RemoveProviderExcept(cloudprovider.GCP)
conf.Provider.GCP.Image = "projects/constellation-images/global/images/constellation-v1-3-0"
return conf
}(),
want: false,
},
"gcp debug": {
conf: func() *Config {
conf := Default()
conf.RemoveProviderExcept(cloudprovider.GCP)
conf.Provider.GCP.Image = "projects/constellation-images/global/images/constellation-20220812102023"
return conf
}(),
want: true,
},
"azure release": {
conf: func() *Config {
conf := Default()
conf.RemoveProviderExcept(cloudprovider.Azure)
conf.Provider.Azure.Image = "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.Compute/galleries/Constellation/images/constellation/versions/2022.0805.151600"
return conf
}(),
want: false,
},
"azure debug": {
conf: func() *Config {
conf := Default()
conf.RemoveProviderExcept(cloudprovider.Azure)
conf.Provider.Azure.Image = "/subscriptions/0d202bbb-4fa7-4af8-8125-58c269a05435/resourceGroups/constellation-images/providers/Microsoft.Compute/galleries/Constellation_Debug/images/v1.4.0/versions/2022.0805.151600"
return conf
}(),
want: true,
},
"empty config": {
conf: &Config{},
want: false,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
assert.Equal(tc.want, tc.conf.IsImageDebug())
})
}
}