Skip to content

Commit

Permalink
feat: gitlab integration
Browse files Browse the repository at this point in the history
Signed-off-by: Batuhan Apaydın <[email protected]>
  • Loading branch information
developer-guy committed Oct 5, 2021
1 parent fca0723 commit f8f388b
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 26 deletions.
12 changes: 8 additions & 4 deletions cmd/cosign/cli/generate/generate_key_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,22 @@ func GenerateKeyPairCmd(ctx context.Context, kmsVal string, args []string) error
}

if len(args) > 0 {
// github://repository/project
split := strings.Split(args[0], "://")

if len(split) < 2 {
return errors.New("could not parse scheme, use <protocol>://<ref> format")
}

provider, targetRef := split[0], split[1]

switch provider {
case "k8s":
return kubernetes.KeyPairSecret(ctx, targetRef, GetPass)
case gitlab.GitLabReference,github.GitHubReference:
return git.GetGit(provider).PutSecret(ctx, targetRef, GetPass)
case gitlab.ReferenceScheme, github.ReferenceScheme:
return git.GetProvider(provider).PutSecret(ctx, targetRef, GetPass)
}

return fmt.Errorf("undefined provider")
return fmt.Errorf("undefined provider: %s", provider)
}

keys, err := cosign.GenerateKeyPair(GetPass)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ require (
github.com/spf13/cobra v1.2.1
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613
github.com/urfave/cli v1.22.5 // indirect
github.com/xanzy/go-gitlab v0.31.0
go.opentelemetry.io/contrib v0.22.0 // indirect
go.opentelemetry.io/proto/otlp v0.9.0 // indirect
golang.org/x/mod v0.5.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1619,6 +1619,7 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/xanzy/go-gitlab v0.31.0 h1:+nHztQuCXGSMluKe5Q9IRaPdz6tO8O0gMkQ0vqGpiBk=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
Expand Down
22 changes: 20 additions & 2 deletions pkg/cosign/git/git.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package git

import (
"context"

"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/git/github"
"github.com/sigstore/cosign/pkg/cosign/git/gitlab"
)

var providerMap = map[string]Git{
github.GitHubReference: github.New(),
github.ReferenceScheme: github.New(),
gitlab.ReferenceScheme: gitlab.New(),
}

type Git interface {
PutSecret(ctx context.Context, ref string, pf cosign.PassFunc) error
}

func GetGit(provider string) Git {
func GetProvider(provider string) Git {
return providerMap[provider]
}
91 changes: 73 additions & 18 deletions pkg/cosign/git/github/github.go
Original file line number Diff line number Diff line change
@@ -1,64 +1,119 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package github

import (
"context"
"encoding/base64"
"fmt"
"github.com/google/go-github/v39/github"
"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign"
"golang.org/x/oauth2"
"io"
"io/ioutil"
"net/http"
"os"
"strings"

gogithub "github.com/google/go-github/v39/github"
"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign"
"golang.org/x/oauth2"
)

const (
GitHubReference = "github"
ReferenceScheme = "github"
)

type gh struct{}
type github struct{}

func New() *gh {
return &gh{}
func New() *github {
return &github{}
}

func (g *gh) PutSecret(ctx context.Context, ref string, pf cosign.PassFunc) error {
func (g *github) PutSecret(ctx context.Context, ref string, pf cosign.PassFunc) error {
keys, err := cosign.GenerateKeyPair(pf)
if err != nil {
return errors.Wrap(err, "generating key pair")
}

var httpClient *http.Client
if token, ok := os.LookupEnv("GITHUB_TOKEN"); ok { // todo: if not ok then return error
if token, ok := os.LookupEnv("GITHUB_TOKEN"); ok {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
httpClient = oauth2.NewClient(ctx, ts)
} else {
return errors.New("could not find \"GITHUB_TOKEN\" env variable")
}
client := github.NewClient(httpClient)
client := gogithub.NewClient(httpClient)

split := strings.Split(ref, "/")
owner, repo := split[0], split[1] // todo: check second element
if len(split) < 2 {
return errors.New("could not parse scheme, use github://<owner>/<repo> format")
}
owner, repo := split[0], split[1]

key, _, err := client.Actions.GetRepoPublicKey(ctx, owner, repo) // todo: check response status
key, getRepoPubKeyResp, err := client.Actions.GetRepoPublicKey(ctx, owner, repo)
if err != nil {
return errors.Wrap(err, "could not get repository public key")
}

secret := &github.EncryptedSecret{
if getRepoPubKeyResp.StatusCode < 200 && getRepoPubKeyResp.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(getRepoPubKeyResp.Body)
return fmt.Errorf("%s", bodyBytes)
}

passwordSecretEnv := &gogithub.EncryptedSecret{
Name: "COSIGN_PASSWORD",
KeyID: key.GetKeyID(),
EncryptedValue: base64.StdEncoding.EncodeToString(keys.Password()),
}

passwordSecretEnvResp, err := client.Actions.CreateOrUpdateRepoSecret(ctx, owner, repo, passwordSecretEnv)
if err != nil {
return errors.Wrap(err, "could not create \"COSIGN_PASSWORD\" environment variable")
}

if passwordSecretEnvResp.StatusCode < 200 && passwordSecretEnvResp.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(passwordSecretEnvResp.Body)
return fmt.Errorf("%s", bodyBytes)
}

fmt.Fprintln(os.Stderr, "Private key written to COSIGN_PASSWORD environment variable")

privateKeySecretEnv := &gogithub.EncryptedSecret{
Name: "COSIGN_PRIVATE_KEY",
KeyID: key.GetKeyID(),
EncryptedValue: base64.StdEncoding.EncodeToString(keys.PrivateBytes),
}

repoSecret, err := client.Actions.CreateOrUpdateRepoSecret(ctx, owner, repo, secret)
repoSecretEnvResp, err := client.Actions.CreateOrUpdateRepoSecret(ctx, owner, repo, privateKeySecretEnv)
if err != nil {
return err
return errors.Wrap(err, "could not create \"COSIGN_PRIVATE_KEY\" environment variable")
}

if repoSecret.StatusCode < 200 && repoSecret.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(repoSecret.Body)
if repoSecretEnvResp.StatusCode < 200 && repoSecretEnvResp.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(repoSecretEnvResp.Body)
return fmt.Errorf("%s", bodyBytes)
}

fmt.Fprintln(os.Stderr, "Private key written to COSIGN_PRIVATE_KEY environment variable")

if err := ioutil.WriteFile("cosign.pub", keys.PublicBytes, 0o600); err != nil {
return err
}
fmt.Fprintln(os.Stderr, "Public key written to cosign.pub")

return nil
}
105 changes: 104 additions & 1 deletion pkg/cosign/git/gitlab/gitlab.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,108 @@
//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gitlab

import (
"context"
"fmt"
"io"
"io/ioutil"
"os"

"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign"
gogitlab "github.com/xanzy/go-gitlab"
)

const (
GitLabReference = "gitlab"
ReferenceScheme = "gitlab"
)

type gitlab struct{}

func New() *gitlab {
return &gitlab{}
}

func (g *gitlab) PutSecret(ctx context.Context, ref string, pf cosign.PassFunc) error {
keys, err := cosign.GenerateKeyPair(pf)
if err != nil {
return errors.Wrap(err, "generating key pair")
}

token, tokenExists := os.LookupEnv("GITLAB_TOKEN")

if !tokenExists {
return errors.New("could not find \"GITLAB_TOKEN\" env variable")
}

var client *gogitlab.Client
if url, baseURLExists := os.LookupEnv("GITLAB_BASE_URL"); baseURLExists {
client, err = gogitlab.NewClient(token, gogitlab.WithBaseURL(url))
if err != nil {
return errors.Wrap(err, "could not create GitLab client")
}
} else {
client, err = gogitlab.NewClient(token)
if err != nil {
return errors.Wrap(err, "could not create GitLab client")
}
}

_, passwordResp, err := client.ProjectVariables.CreateVariable(ref, &gogitlab.CreateProjectVariableOptions{
Key: gogitlab.String("COSIGN_PASSWORD"),
Value: gogitlab.String(string(keys.Password())),
VariableType: gogitlab.VariableType(gogitlab.EnvVariableType),
Protected: gogitlab.Bool(false),
Masked: gogitlab.Bool(false),
EnvironmentScope: gogitlab.String("*"),
})
if err != nil {
return errors.Wrap(err, "could not create \"COSIGN_PASSWORD\" environment variable")
}

if passwordResp.StatusCode < 200 && passwordResp.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(passwordResp.Body)
return errors.Errorf("%s", bodyBytes)
}

fmt.Fprintln(os.Stderr, "Password written to COSIGN_PASSWORD environment variable")

_, privateKeyResp, err := client.ProjectVariables.CreateVariable(ref, &gogitlab.CreateProjectVariableOptions{
Key: gogitlab.String("COSIGN_PRIVATE_KEY"),
Value: gogitlab.String(string(keys.PrivateBytes)),
VariableType: gogitlab.VariableType(gogitlab.EnvVariableType),
Protected: gogitlab.Bool(false),
Masked: gogitlab.Bool(false),
})
if err != nil {
return errors.Wrap(err, "could not create \"COSIGN_PRIVATE_KEY\" environment variable")
}

if privateKeyResp.StatusCode < 200 && privateKeyResp.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(privateKeyResp.Body)
return errors.Errorf("%s", bodyBytes)
}

fmt.Fprintln(os.Stderr, "Private key written to COSIGN_PRIVATE_KEY environment variable")

if err := ioutil.WriteFile("cosign.pub", keys.PublicBytes, 0o600); err != nil {
return err
}
fmt.Fprintln(os.Stderr, "Public key written to cosign.pub")

return nil
}
1 change: 0 additions & 1 deletion pkg/cosign/kubernetes/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (

const (
KeyReference = "k8s://"

)

func GetKeyPairSecret(ctx context.Context, k8sRef string) (*v1.Secret, error) {
Expand Down

0 comments on commit f8f388b

Please sign in to comment.