mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-22 16:36:30 +00:00
support for .drone.sec file
This commit is contained in:
parent
d4c7e7bf59
commit
b65d1cda97
13 changed files with 80 additions and 125 deletions
|
@ -119,9 +119,8 @@
|
|||
|
||||
|
||||
$scope.encrypt = function (plaintext) {
|
||||
var data = {"DATA": plaintext};
|
||||
repos.encrypt(fullName, data).then(function (payload) {
|
||||
$scope.secure = payload.data["DATA"];
|
||||
repos.encrypt(fullName, plaintext).then(function (payload) {
|
||||
$scope.secure = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
|
|
@ -77,10 +77,10 @@
|
|||
* Encrypt the set of parameters.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
* @param {object} Key/Value map of parameters.
|
||||
* @param {string} Plaintext to encrypt.
|
||||
*/
|
||||
this.encrypt = function (repoName, params) {
|
||||
return $http.post('/api/repos/' + repoName + '/encrypt', params);
|
||||
this.encrypt = function (repoName, plaintext) {
|
||||
return $http.post('/api/repos/' + repoName + '/encrypt', plaintext);
|
||||
};
|
||||
|
||||
var callback,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<article>
|
||||
|
||||
<p style=" font-size: 16px;
|
||||
margin-bottom: 10px;">Encrypt and store secret variables in your <code>.drone.yml</code> file<p>
|
||||
margin-bottom: 10px;">Encrypt and store secret variables in the <code>.drone.sec</code> file<p>
|
||||
<textarea spellcheck="false" placeholder="Enter a plaintext value here" ng-model="plaintext" style="
|
||||
background-color: #eff1f5;
|
||||
border-radius:3px;
|
||||
|
|
|
@ -15,7 +15,8 @@ type Work struct {
|
|||
Build *common.Build `json:"build"`
|
||||
Keys *common.Keypair `json:"keypair"`
|
||||
Netrc *common.Netrc `json:"netrc"`
|
||||
Yaml []byte `json:"yaml"`
|
||||
Config []byte `json:"config"`
|
||||
Secret []byte `json:"secret"`
|
||||
}
|
||||
|
||||
// represents a worker that has connected
|
||||
|
|
|
@ -163,9 +163,12 @@ func (g *GitHub) Perm(u *common.User, owner, name string) (*common.Perm, error)
|
|||
|
||||
// Script fetches the build script (.drone.yml) from the remote
|
||||
// repository and returns in string format.
|
||||
func (g *GitHub) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error) {
|
||||
func (g *GitHub) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, []byte, error) {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
return GetFile(client, r.Owner, r.Name, ".drone.yml", b.Commit.Sha)
|
||||
|
||||
cfg, err := GetFile(client, r.Owner, r.Name, ".drone.yml", b.Commit.Sha)
|
||||
sec, _ := GetFile(client, r.Owner, r.Name, ".drone.sec", b.Commit.Sha)
|
||||
return cfg, sec, err
|
||||
}
|
||||
|
||||
// Netrc returns a .netrc file that can be used to clone
|
||||
|
|
|
@ -159,14 +159,16 @@ func (g *Gitlab) Perm(u *common.User, owner, name string) (*common.Perm, error)
|
|||
|
||||
// GetScript fetches the build script (.drone.yml) from the remote
|
||||
// repository and returns in string format.
|
||||
func (g *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Build) ([]byte, error) {
|
||||
func (g *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Build) ([]byte, []byte, error) {
|
||||
var client = NewClient(g.URL, user.Token, g.SkipVerify)
|
||||
id, err := GetProjectId(g, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.RepoRawFile(id, build.Commit.Sha, ".drone.yml")
|
||||
cfg, err := client.RepoRawFile(id, build.Commit.Sha, ".drone.yml")
|
||||
enc, _ := client.RepoRawFile(id, build.Commit.Sha, ".drone.sec")
|
||||
return cfg, enc, err
|
||||
}
|
||||
|
||||
// NOTE Currently gitlab doesn't support status for commits and events,
|
||||
|
|
|
@ -56,7 +56,7 @@ type Remote interface {
|
|||
|
||||
// Script fetches the build script (.drone.yml) from the remote
|
||||
// repository and returns in string format.
|
||||
Script(u *types.User, r *types.Repo, b *types.Build) ([]byte, error)
|
||||
Script(u *types.User, r *types.Repo, b *types.Build) ([]byte, []byte, error)
|
||||
|
||||
// Status sends the commit status to the remote system.
|
||||
// An example would be the GitHub pull request status.
|
||||
|
|
|
@ -144,7 +144,8 @@ func (r *Runner) Run(w *queue.Work) error {
|
|||
Repo: w.Repo,
|
||||
Build: w.Build,
|
||||
Job: job,
|
||||
Yaml: string(w.Yaml),
|
||||
Secret: string(w.Secret),
|
||||
Config: string(w.Config),
|
||||
}
|
||||
in, err := json.Marshal(work)
|
||||
if err != nil {
|
||||
|
@ -224,7 +225,8 @@ func (r *Runner) Run(w *queue.Work) error {
|
|||
Repo: w.Repo,
|
||||
Build: w.Build,
|
||||
Job: job,
|
||||
Yaml: string(w.Yaml),
|
||||
Secret: string(w.Secret),
|
||||
Config: string(w.Config),
|
||||
}
|
||||
in, err := json.Marshal(work)
|
||||
if err != nil {
|
||||
|
|
|
@ -50,7 +50,8 @@ type work struct {
|
|||
Job *types.Job `json:"job"`
|
||||
System *types.System `json:"system"`
|
||||
Workspace *types.Workspace `json:"workspace"`
|
||||
Yaml string `json:"yaml"`
|
||||
Secret string `json:"secret"`
|
||||
Config string `json:"config"`
|
||||
}
|
||||
|
||||
type worker struct {
|
||||
|
|
|
@ -6,14 +6,10 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
|
||||
"github.com/drone/drone/pkg/queue"
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
"github.com/drone/drone/pkg/utils/httputil"
|
||||
"github.com/drone/drone/pkg/yaml/inject"
|
||||
"github.com/drone/drone/pkg/yaml/secure"
|
||||
// "github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
// GetCommit accepts a request to retrieve a commit
|
||||
|
@ -175,33 +171,22 @@ func RunBuild(c *gin.Context) {
|
|||
}
|
||||
|
||||
// featch the .drone.yml file from the database
|
||||
raw, err := remote.Script(user, repo, build)
|
||||
raw, sec, err := remote.Script(user, repo, build)
|
||||
if err != nil {
|
||||
c.Fail(404, err)
|
||||
return
|
||||
}
|
||||
|
||||
// inject any private parameters into the .drone.yml
|
||||
if repo.Params != nil && len(repo.Params) != 0 {
|
||||
raw = []byte(inject.InjectSafe(string(raw), repo.Params))
|
||||
}
|
||||
encrypted, err := secure.Parse(repo.Keys.Private, repo.Hash, string(raw))
|
||||
if err != nil {
|
||||
log.Errorf("failure to decrypt secure parameters for %s. %s", repo.FullName, err)
|
||||
}
|
||||
if encrypted != nil && len(encrypted) != 0 {
|
||||
raw = []byte(inject.InjectSafe(string(raw), encrypted))
|
||||
}
|
||||
|
||||
c.JSON(202, build)
|
||||
|
||||
queue_.Publish(&queue.Work{
|
||||
User: user,
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
Keys: repo.Keys,
|
||||
Netrc: netrc,
|
||||
Yaml: raw,
|
||||
User: user,
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
Keys: repo.Keys,
|
||||
Netrc: netrc,
|
||||
Config: raw,
|
||||
Secret: sec,
|
||||
System: &common.System{
|
||||
Link: httputil.GetURL(c.Request),
|
||||
Plugins: conf.Plugins,
|
||||
|
|
|
@ -10,9 +10,7 @@ import (
|
|||
common "github.com/drone/drone/pkg/types"
|
||||
"github.com/drone/drone/pkg/utils/httputil"
|
||||
"github.com/drone/drone/pkg/yaml"
|
||||
"github.com/drone/drone/pkg/yaml/inject"
|
||||
"github.com/drone/drone/pkg/yaml/matrix"
|
||||
"github.com/drone/drone/pkg/yaml/secure"
|
||||
)
|
||||
|
||||
// PostHook accepts a post-commit hook and parses the payload
|
||||
|
@ -93,25 +91,13 @@ func PostHook(c *gin.Context) {
|
|||
build.RepoID = repo.ID
|
||||
|
||||
// fetch the .drone.yml file from the database
|
||||
raw, err := remote.Script(user, repo, build)
|
||||
raw, sec, err := remote.Script(user, repo, build)
|
||||
if err != nil {
|
||||
log.Errorf("failure to get .drone.yml for %s. %s", repo.FullName, err)
|
||||
c.Fail(404, err)
|
||||
return
|
||||
}
|
||||
// inject any private parameters into the .drone.yml
|
||||
if repo.Params != nil && len(repo.Params) != 0 {
|
||||
raw = []byte(inject.InjectSafe(string(raw), repo.Params))
|
||||
}
|
||||
encrypted, err := secure.Parse(repo.Keys.Private, repo.Hash, string(raw))
|
||||
if err != nil {
|
||||
log.Errorf("failure to decrypt secure parameters for %s. %s", repo.FullName, err)
|
||||
c.Fail(400, err)
|
||||
return
|
||||
}
|
||||
if encrypted != nil && len(encrypted) != 0 {
|
||||
raw = []byte(inject.InjectSafe(string(raw), encrypted))
|
||||
}
|
||||
|
||||
axes, err := matrix.Parse(string(raw))
|
||||
if err != nil {
|
||||
log.Errorf("failure to calculate matrix for %s. %s", repo.FullName, err)
|
||||
|
@ -160,12 +146,13 @@ func PostHook(c *gin.Context) {
|
|||
}
|
||||
|
||||
queue_.Publish(&queue.Work{
|
||||
User: user,
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
Keys: repo.Keys,
|
||||
Netrc: netrc,
|
||||
Yaml: raw,
|
||||
User: user,
|
||||
Repo: repo,
|
||||
Build: build,
|
||||
Keys: repo.Keys,
|
||||
Netrc: netrc,
|
||||
Config: raw,
|
||||
Secret: sec,
|
||||
System: &common.System{
|
||||
Link: httputil.GetURL(c.Request),
|
||||
Plugins: conf.Plugins,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin/binding"
|
||||
|
@ -249,16 +249,17 @@ func PostRepo(c *gin.Context) {
|
|||
//
|
||||
func Encrypt(c *gin.Context) {
|
||||
repo := ToRepo(c)
|
||||
|
||||
in := map[string]string{}
|
||||
json.NewDecoder(c.Request.Body).Decode(&in)
|
||||
privKey := sshutil.UnMarshalPrivateKey([]byte(repo.Keys.Private))
|
||||
err := secure.EncryptMap(secure.ToHash(repo.Hash), &privKey.PublicKey, in)
|
||||
in, err := ioutil.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.Fail(500, err)
|
||||
return
|
||||
}
|
||||
c.JSON(200, &in)
|
||||
out, err := secure.Encrypt(string(in), repo.Keys.Private)
|
||||
if err != nil {
|
||||
c.Fail(500, err)
|
||||
return
|
||||
}
|
||||
c.Writer.Write([]byte(out))
|
||||
}
|
||||
|
||||
// Unsubscribe accapets a request to unsubscribe the
|
||||
|
|
|
@ -2,71 +2,45 @@ package secure
|
|||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"hash"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/drone/drone/pkg/utils/sshutil"
|
||||
"github.com/square/go-jose"
|
||||
)
|
||||
|
||||
// Parse parses and returns the secure section of the
|
||||
// yaml file as plaintext parameters.
|
||||
func Parse(privateKeyPEM, repoHash, raw string) (map[string]string, error) {
|
||||
params, err := parseSecure(raw)
|
||||
// Encrypt encrypts a secret string.
|
||||
func Encrypt(in, privKey string) (string, error) {
|
||||
rsaPrivKey, err := decodePrivateKey(privKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
hasher := ToHash(repoHash)
|
||||
privKey := sshutil.UnMarshalPrivateKey([]byte(privateKeyPEM))
|
||||
|
||||
err = DecryptMap(hasher, privKey, params)
|
||||
return params, err
|
||||
return encrypt(in, &rsaPrivKey.PublicKey)
|
||||
}
|
||||
|
||||
// DecryptMap decrypts values of a map of named parameters
|
||||
// from base64 to decrypted strings.
|
||||
func DecryptMap(hasher hash.Hash, privKey *rsa.PrivateKey, params map[string]string) error {
|
||||
var err error
|
||||
// decodePrivateKey is a helper function that unmarshals a PEM
|
||||
// bytes to an RSA Private Key
|
||||
func decodePrivateKey(privateKey string) (*rsa.PrivateKey, error) {
|
||||
derBlock, _ := pem.Decode([]byte(privateKey))
|
||||
return x509.ParsePKCS1PrivateKey(derBlock.Bytes)
|
||||
}
|
||||
|
||||
for name, encrypted := range params {
|
||||
params[name], err = sshutil.Decrypt(hasher, privKey, encrypted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// encrypt encrypts a plaintext variable using JOSE with
|
||||
// RSA_OAEP and A128GCM algorithms.
|
||||
func encrypt(text string, pubKey *rsa.PublicKey) (string, error) {
|
||||
var encrypted string
|
||||
var plaintext = []byte(text)
|
||||
|
||||
// Creates a new encrypter using defaults
|
||||
encrypter, err := jose.NewEncrypter(jose.RSA_OAEP, jose.A128GCM, pubKey)
|
||||
if err != nil {
|
||||
return encrypted, err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncryptMap encrypts values of a map of named parameters
|
||||
func EncryptMap(hasher hash.Hash, pubKey *rsa.PublicKey, params map[string]string) error {
|
||||
var err error
|
||||
|
||||
for name, value := range params {
|
||||
params[name], err = sshutil.Encrypt(hasher, pubKey, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Encrypts the plaintext value and serializes
|
||||
// as a JOSE string.
|
||||
object, err := encrypter.Encrypt(plaintext)
|
||||
if err != nil {
|
||||
return encrypted, err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseSecure is helper function to parse the Secure data from
|
||||
// the raw yaml file.
|
||||
func parseSecure(raw string) (map[string]string, error) {
|
||||
data := struct {
|
||||
Secure map[string]string
|
||||
}{}
|
||||
err := yaml.Unmarshal([]byte(raw), &data)
|
||||
|
||||
return data.Secure, err
|
||||
}
|
||||
|
||||
// ToHash is helper function to generate Hash of given string
|
||||
func ToHash(key string) hash.Hash {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(key))
|
||||
hasher.Reset()
|
||||
return hasher
|
||||
return object.CompactSerialize()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue