This repository has been archived by the owner on Jun 2, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
main.go
109 lines (96 loc) · 3.14 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// terraform-credentials-env is a Terraform credentials helper that reads
// credentials from the process environment.
//
// Specifically, it expects to find environment variables with the prefix
// TF_TOKEN_ followed by the requested hostname, such as
// TF_TOKEN_app.terraform.io for Terraform Cloud.
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
svchost "github.com/hashicorp/terraform-svchost"
)
var GitCommit = ""
var Version = "0.0.0"
var PreRelease = "dev"
func main() {
creds := collectCredentialsFromEnv()
args := os.Args[1:]
if len(args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: terraform-credentials-env get <hostname>")
fmt.Fprintln(os.Stderr, "\nThis is a Terraform credentials helper, not intended to be run directly from a shell.")
os.Exit(1)
}
switch args[0] {
case "get":
// The credentials helper protocol calls for Terraform to provide the
// hostname already in the "for comparison" form, so we'll assume that
// here and let this not match if the caller isn't behaving.
wantedHost := svchost.Hostname(args[1])
token, ok := creds[wantedHost]
if !ok {
// No stored credentials for a host is a non-error case; respond
// with an empty credentials object.
os.Stdout.WriteString("{}\n")
os.Exit(0)
}
result := resultJSON{token}
resultJSON, err := json.Marshal(result)
if err != nil {
// Should never happen
fmt.Fprintf(os.Stderr, "Failed to serialize result: %s\n", err)
os.Exit(1)
}
os.Stdout.Write(resultJSON)
os.Stdout.WriteString("\n")
os.Exit(0)
default:
fmt.Fprintf(os.Stderr, "The 'env' credentials helper is not able to %s credentials.\n", args[0])
os.Exit(1)
}
}
func collectCredentialsFromEnv() map[svchost.Hostname]string {
const prefix = "TF_TOKEN_"
ret := make(map[svchost.Hostname]string)
for _, ev := range os.Environ() {
eqIdx := strings.Index(ev, "=")
if eqIdx < 0 {
continue
}
name := ev[:eqIdx]
value := ev[eqIdx+1:]
if !strings.HasPrefix(name, prefix) {
continue
}
rawHost := name[len(prefix):]
// We accept underscores in place of dots because dots are not valid
// identifiers in most shells and are therefore hard to set.
// Underscores are not valid in hostnames, so this is unambiguous for
// valid hostnames.
rawHost = strings.ReplaceAll(rawHost, "_", ".")
// Because environment variables are often set indirectly by OS
// libraries that might interfere with how they are encoded, we'll
// be tolerant of them being given either directly as UTF-8 IDNs
// or in Punycode form, normalizing to Punycode form here because
// that is what the Terraform credentials helper protocol will
// use in its requests.
//
// Using ForDisplay first here makes this more liberal than Terraform
// itself would usually be in that it will tolerate pre-punycoded
// hostnames that Terraform normally rejects in other contexts in order
// to ensure stored hostnames are human-readable.
dispHost := svchost.ForDisplay(rawHost)
hostname, err := svchost.ForComparison(dispHost)
if err != nil {
// Ignore invalid hostnames
continue
}
ret[hostname] = value
}
return ret
}
type resultJSON struct {
Token string `json:"token"`
}