mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-27 09:21:14 -06:00
981c95f699
This CredentialsSource can serve as an extension point to pass credentials from an arbitrary external system to Terraform. For example, an external helper program could fetch limited-time credentials from HashiCorp Vault and return them, thus avoiding the need for any static configuration to be maintained locally (except a Vault token!). So far there are no real programs implementing this protocol, though this commit includes a basic implementation that we use for unit tests.
81 lines
2.3 KiB
Go
81 lines
2.3 KiB
Go
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/hashicorp/terraform/svchost"
|
|
)
|
|
|
|
type helperProgramCredentialsSource struct {
|
|
executable string
|
|
args []string
|
|
}
|
|
|
|
// HelperProgramCredentialsSource returns a CredentialsSource that runs the
|
|
// given program with the given arguments in order to obtain credentials.
|
|
//
|
|
// The given executable path must be an absolute path; it is the caller's
|
|
// responsibility to validate and process a relative path or other input
|
|
// provided by an end-user. If the given path is not absolute, this
|
|
// function will panic.
|
|
//
|
|
// When credentials are requested, the program will be run in a child process
|
|
// with the given arguments along with two additional arguments added to the
|
|
// end of the list: the literal string "get", followed by the requested
|
|
// hostname in ASCII compatibility form (punycode form).
|
|
func HelperProgramCredentialsSource(executable string, args ...string) CredentialsSource {
|
|
if !filepath.IsAbs(executable) {
|
|
panic("NewCredentialsSourceHelperProgram requires absolute path to executable")
|
|
}
|
|
|
|
fullArgs := make([]string, len(args)+1)
|
|
fullArgs[0] = executable
|
|
copy(fullArgs[1:], args)
|
|
|
|
return &helperProgramCredentialsSource{
|
|
executable: executable,
|
|
args: fullArgs,
|
|
}
|
|
}
|
|
|
|
func (s *helperProgramCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) {
|
|
args := make([]string, len(s.args), len(s.args)+2)
|
|
copy(args, s.args)
|
|
args = append(args, "get")
|
|
args = append(args, string(host))
|
|
|
|
outBuf := bytes.Buffer{}
|
|
errBuf := bytes.Buffer{}
|
|
|
|
cmd := exec.Cmd{
|
|
Path: s.executable,
|
|
Args: args,
|
|
Stdin: nil,
|
|
Stdout: &outBuf,
|
|
Stderr: &errBuf,
|
|
}
|
|
err := cmd.Run()
|
|
if _, isExitErr := err.(*exec.ExitError); isExitErr {
|
|
errText := errBuf.String()
|
|
if errText == "" {
|
|
// Shouldn't happen for a well-behaved helper program
|
|
return nil, fmt.Errorf("error in %s, but it produced no error message", s.executable)
|
|
}
|
|
return nil, fmt.Errorf("error in %s: %s", s.executable, errText)
|
|
} else if err != nil {
|
|
return nil, fmt.Errorf("failed to run %s: %s", s.executable, err)
|
|
}
|
|
|
|
var m map[string]interface{}
|
|
err = json.Unmarshal(outBuf.Bytes(), &m)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("malformed output from %s: %s", s.executable, err)
|
|
}
|
|
|
|
return HostCredentialsFromMap(m), nil
|
|
}
|