mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
* [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
129 lines
3.7 KiB
Go
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
|
|
}
|