forked from harness/drone-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
secure.go
161 lines (142 loc) · 3.56 KB
/
secure.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package main
import (
"crypto/rsa"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"github.com/codegangsta/cli"
"github.com/drone/drone-exec/yaml/secure"
"github.com/drone/drone-go/drone"
"github.com/square/go-jose"
"golang.org/x/crypto/ssh"
"gopkg.in/yaml.v2"
)
var SecureCmd = cli.Command{
Name: "secure",
Usage: "creates a secure yaml file",
Action: func(c *cli.Context) {
handle(c, SecureYamlCmd)
},
Flags: []cli.Flag{
cli.StringFlag{
Name: "in",
Usage: "input path to the plaintext secret file (use - for stdin)",
Value: ".drone.sec.yml",
},
cli.StringFlag{
Name: "out",
Usage: "output path for the encrypted secret file (use - for stdout)",
Value: ".drone.sec",
},
cli.StringFlag{
Name: "repo",
Usage: "name of the repository",
},
cli.StringFlag{
Name: "yaml",
Usage: "path to .drone.yml file",
Value: ".drone.yml",
},
cli.BoolTFlag{
Name: "checksum",
Usage: "calculate and encrypt the yaml checksum",
},
},
}
func SecureYamlCmd(c *cli.Context, client drone.Client) error {
var (
repo = c.String("repo")
inFile = c.String("in")
outFile = c.String("out")
ymlFile = c.String("yaml")
checksum = c.BoolT("checksum")
)
owner, name, err := parseRepo(repo)
if err != nil {
return err
}
keypair, err := client.RepoKey(owner, name)
if err != nil {
return err
}
key, err := toPublicKey(keypair.Public)
if err != nil {
return err
}
// read the .drone.sec.yml file (plain text)
plaintext, err := readInput(inFile)
if err != nil {
return err
}
// parse the .drone.sec.yml file
sec := new(secure.Secure)
err = yaml.Unmarshal(plaintext, sec)
if err != nil {
return err
}
// read the .drone.yml file and caclulate the
// checksum. add to the .drone.sec.yml file.
yml, err := ioutil.ReadFile(ymlFile)
if err == nil && checksum {
sec.Checksum = sha256sum(string(yml))
}
// re-marshal the .drone.sec.yml file since we've
// added the checksum
plaintext, err = yaml.Marshal(sec)
if err != nil {
return err
}
// encrypt the .drone.sec.yml file
ciphertext, err := encrypt(plaintext, key)
if err != nil {
return err
}
// write the encrypted .drone.sec.yml file to .drone.sec
return writeOutput(outFile, ciphertext)
}
// toPublicKey parses a public key and returns an *rsa.PublicKey.
// credit to stackoverflow http://stackoverflow.com/q/31593329
func toPublicKey(key string) (*rsa.PublicKey, error) {
raw := []byte(key)
pub, _, _, _, err := ssh.ParseAuthorizedKey(raw)
if err != nil {
return nil, err
}
return reflect.ValueOf(pub).
Convert(reflect.TypeOf(new(rsa.PublicKey))).Interface().(*rsa.PublicKey), nil
}
// encrypt encrypts a plaintext variable using JOSE with
// RSA_OAEP and A128GCM algorithms.
func encrypt(plaintext []byte, pubKey *rsa.PublicKey) (string, error) {
var encrypted string
// Creates a new encrypter using defaults
encrypter, err := jose.NewEncrypter(jose.RSA_OAEP, jose.A128GCM, pubKey)
if err != nil {
return encrypted, err
}
// Encrypts the plaintext value and serializes
// as a JOSE string.
object, err := encrypter.Encrypt(plaintext)
if err != nil {
return encrypted, err
}
return object.CompactSerialize()
}
func sha256sum(in string) string {
h := sha256.New()
io.WriteString(h, in)
return fmt.Sprintf("%x", h.Sum(nil))
}
// writeOutput writes the encrypted secret to a file
// or stdout if outFile is -
func writeOutput(outFile string, ciphertext string) error {
if outFile == "-" {
_, err := os.Stdout.Write([]byte(ciphertext))
return err
} else {
return ioutil.WriteFile(outFile, []byte(ciphertext), 0664)
}
}