Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions internal/boxcli/featureflag/envsec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright 2023 Jetpack Technologies Inc and contributors. All rights reserved.
// Use of this source code is governed by the license in the LICENSE file.

package featureflag

var Envsec = disable("ENVSEC")
4 changes: 4 additions & 0 deletions internal/devconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ type Config struct {

// Env allows specifying env variables
Env map[string]string `json:"env,omitempty"`

// Only allows "envsec" for now
FromEnv string `json:"from_env,omitempty"`

// Shell configures the devbox shell environment.
Shell *shellConfig `json:"shell,omitempty"`
// Nixpkgs specifies the repository to pull packages from
Expand Down
26 changes: 26 additions & 0 deletions internal/devconfig/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package devconfig

import (
"go.jetpack.io/devbox/internal/boxcli/featureflag"
"go.jetpack.io/devbox/internal/boxcli/usererr"
"go.jetpack.io/devbox/internal/integrations/envsec"
)

func (c *Config) ComputedEnv(projectDir string) (map[string]string, error) {
env := map[string]string{}
var err error
if featureflag.Envsec.Enabled() {
if c.FromEnv == "envsec" {
env, err = envsec.Env(projectDir)
if err != nil {
return nil, err
}
} else if c.FromEnv != "" {
return nil, usererr.New("unknown from_env value: %s", c.FromEnv)
}
}
for k, v := range c.Env {
env[k] = v
}
return env, nil
}
17 changes: 12 additions & 5 deletions internal/impl/devbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,10 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
}

// Include env variables in devbox.json
configEnv := d.configEnvs(env)
configEnv, err := d.configEnvs(env)
if err != nil {
return nil, err
}
addEnvIfNotPreviouslySetByDevbox(env, configEnv)

markEnvsAsSetByDevbox(pluginEnv, configEnv)
Expand Down Expand Up @@ -1052,14 +1055,18 @@ func (d *Devbox) checkOldEnvrc() error {
return nil
}

// configEnvs takes the computed env variables (nix + plugin) and adds env
// configEnvs takes the existing environment (nix + plugin) and adds env
// variables defined in Config. It also parses variables in config
// that are referenced by $VAR or ${VAR} and replaces them with
// their value in the computed env variables. Note, this doesn't
// their value in the existing env variables. Note, this doesn't
// allow env variables from outside the shell to be referenced so
// no leaked variables are caused by this function.
func (d *Devbox) configEnvs(computedEnv map[string]string) map[string]string {
return conf.OSExpandEnvMap(d.cfg.Env, computedEnv, d.ProjectDir())
func (d *Devbox) configEnvs(existingEnv map[string]string) (map[string]string, error) {
env, err := d.cfg.ComputedEnv(d.ProjectDir())
if err != nil {
return nil, err
}
return conf.OSExpandEnvMap(env, existingEnv, d.ProjectDir()), nil
}

// ignoreCurrentEnvVar contains environment variables that Devbox should remove
Expand Down
63 changes: 63 additions & 0 deletions internal/integrations/envsec/envsec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package envsec

import (
"encoding/json"
"os/exec"

"github.com/pkg/errors"
"go.jetpack.io/devbox/internal/boxcli/usererr"
"go.jetpack.io/devbox/internal/cmdutil"
)

func Env(projectDir string) (map[string]string, error) {

if err := ensureEnvsecInstalled(); err != nil {
return nil, err
}

if err := ensureEnvsecInitialized(); err != nil {
return nil, err
}

return envsecList(projectDir)
}

func ensureEnvsecInstalled() error {
if !cmdutil.Exists("envsec") {
return usererr.New("envsec is not installed or not in path")
}
return nil
}

func ensureEnvsecInitialized() error {
cmd := exec.Command("envsec", "init")
// TODO handle user not logged in
// envsec init is currently broken in that it exits with 0 even if the user is not logged in
return cmd.Run()
}

func envsecList(projectDir string) (map[string]string, error) {
cmd := exec.Command(
"envsec", "ls", "--show",
"--format", "json",
Copy link
Collaborator

@LucilleH LucilleH Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we should do this. I worry that the secrets printed can be discovered through some proc file somewhere.

Can we use the envsec go library directly instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was under impression we wanted to do CLI integration in order to avoid bloating devbox. This has the benefit of allowing other 3rd parties to integrate as well if we provide a standard interface.

If stderr and stdout are captured in a buffer, is there really any risk the outputs ends up anywhere else?

We can also go the RPC route, but that's more work and feels a bit overkill.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking deeper it might be ok based on this line. So I guess we are good

"--environment", "dev")
cmd.Dir = projectDir
out, err := cmd.Output()
if err != nil {
return nil, errors.WithStack(err)
}
var values []struct {
Name string `json:"name"`
Value string `json:"value"`
}

if err := json.Unmarshal(out, &values); err != nil {
return nil, errors.Wrap(err, "failed to parse envsec output")
}

m := map[string]string{}
for _, v := range values {
m[v.Name] = v.Value
}
return m, nil
}