Filip Troníček 65e3f094db
[context parser] Check current ref for Docker image existence (#20345)
* [context parser] Check current ref for Docker image existence

* Better builder bob errors from supervisor

* Gently soft-fail when the Dockerfile isn't found

* Add warning to ws metadata when starting workspace

* Introduce a magic constant instead of empty strings

* Improve supervisor failed reading bob log error

* Fixup cloning our special SHA

* idk what happened

* let workspaces start even on invalid docker refs
2024-11-07 08:13:03 -05:00

129 lines
3.7 KiB
Go

// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.
package builder
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"os"
"path/filepath"
"strings"
"golang.org/x/xerrors"
)
// Config configures a builder
type Config struct {
TargetRef string
BaseRef string
BaseContext string
BuildBase bool
BaseLayerAuth string
WorkspaceLayerAuth string
Dockerfile string
ContextDir string
ExternalBuildkitd string
localCacheImport string
}
var DockerfilePathNotExists = xerrors.Errorf("BOB_DOCKERFILE_PATH does not exist or isn't a file")
// GetConfigFromEnv extracts configuration from environment variables
func GetConfigFromEnv() (*Config, error) {
cfg := &Config{
TargetRef: os.Getenv("BOB_TARGET_REF"),
BaseRef: os.Getenv("BOB_BASE_REF"),
BaseContext: os.Getenv("THEIA_WORKSPACE_ROOT"),
BuildBase: os.Getenv("BOB_BUILD_BASE") == "true",
BaseLayerAuth: os.Getenv("BOB_BASELAYER_AUTH"),
WorkspaceLayerAuth: os.Getenv("BOB_WSLAYER_AUTH"),
Dockerfile: os.Getenv("BOB_DOCKERFILE_PATH"),
ContextDir: os.Getenv("BOB_CONTEXT_DIR"),
ExternalBuildkitd: os.Getenv("BOB_EXTERNAL_BUILDKITD"),
localCacheImport: os.Getenv("BOB_LOCAL_CACHE_IMPORT"),
}
if cfg.BaseRef == "" {
cfg.BaseRef = "localhost:8080/base:latest"
}
if cfg.TargetRef == "" {
cfg.TargetRef = "localhost:8080/target:latest"
}
if cfg.BuildBase {
if cfg.Dockerfile == "" {
return nil, xerrors.Errorf("When building the base image BOB_DOCKERFILE_PATH is mandatory")
}
var err error
cfg.Dockerfile, err = filepath.Abs(cfg.Dockerfile)
if err != nil {
return nil, xerrors.Errorf("cannot make BOB_DOCKERFILE_PATH absolute: %w", err)
}
if !strings.HasPrefix(cfg.Dockerfile, "/workspace") {
return nil, xerrors.Errorf("BOB_DOCKERFILE_PATH must begin with /workspace")
}
if stat, err := os.Stat(cfg.Dockerfile); err != nil || stat.IsDir() {
return nil, DockerfilePathNotExists
}
}
var authKey = os.Getenv("BOB_AUTH_KEY")
if authKey != "" {
if len(authKey) != 32 {
return nil, xerrors.Errorf("BOB_AUTH_KEY must be exactly 32 bytes long")
}
// we have an authkey, hence assume that the auth fields are base64 encoded and encrypted
if cfg.BaseLayerAuth != "" {
dec, err := base64.RawStdEncoding.DecodeString(cfg.BaseLayerAuth)
if err != nil {
return nil, xerrors.Errorf("BOB_BASELAYER_AUTH is not base64 encoded but BOB_AUTH_KEY is present")
}
cfg.BaseLayerAuth, err = decrypt(dec, authKey)
if err != nil {
return nil, xerrors.Errorf("cannot decrypt BOB_BASELAYER_AUTH: %w", err)
}
}
if cfg.WorkspaceLayerAuth != "" {
dec, err := base64.RawStdEncoding.DecodeString(cfg.WorkspaceLayerAuth)
if err != nil {
return nil, xerrors.Errorf("BOB_WSLAYER_AUTH is not base64 encoded but BOB_AUTH_KEY is present")
}
cfg.WorkspaceLayerAuth, err = decrypt(dec, authKey)
if err != nil {
return nil, xerrors.Errorf("cannot decrypt BOB_WSLAYER_AUTH: %w", err)
}
}
}
return cfg, nil
}
// source: https://astaxie.gitbooks.io/build-web-application-with-golang/en/09.6.html
func decrypt(ciphertext []byte, key string) (string, error) {
c, err := aes.NewCipher([]byte(key))
if err != nil {
return "", err
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return "", err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return "", xerrors.Errorf("ciphertext too short")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
res, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", err
}
return string(res), nil
}