Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add github&gitlab reference support to generate-key-pair #848

Merged
merged 6 commits into from
Oct 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion cmd/cosign/cli/generate/generate_key_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import (
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign/git"
"github.com/sigstore/cosign/pkg/cosign/git/github"
"github.com/sigstore/cosign/pkg/cosign/git/gitlab"
"golang.org/x/term"

"github.com/sigstore/cosign/pkg/cosign"
Expand Down Expand Up @@ -57,8 +61,24 @@ func GenerateKeyPairCmd(ctx context.Context, kmsVal string, args []string) error
fmt.Fprintln(os.Stderr, "Public key written to cosign.pub")
return nil
}

if len(args) > 0 {
return kubernetes.KeyPairSecret(ctx, args[0], GetPass)
split := strings.Split(args[0], "://")

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

provider, targetRef := split[0], split[1]
developer-guy marked this conversation as resolved.
Show resolved Hide resolved

switch provider {
case "k8s":
developer-guy marked this conversation as resolved.
Show resolved Hide resolved
return kubernetes.KeyPairSecret(ctx, targetRef, GetPass)
case gitlab.ReferenceScheme, github.ReferenceScheme:
return git.GetProvider(provider).PutSecret(ctx, targetRef, GetPass)
}

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

keys, err := cosign.GenerateKeyPair(GetPass)
Expand Down
9 changes: 9 additions & 0 deletions cmd/cosign/cli/generate_key_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ func addGenerateKeyPair(topLevel *cobra.Command) {
# generate a key-pair in Kubernetes Secret
cosign generate-key-pair k8s://[NAMESPACE]/[NAME]

# generate a key-pair in GitHub
cosign generate-key-pair github://[OWNER]/[PROJECT_NAME]

# generate a key-pair in GitLab with project name
cosign generate-key-pair gitlab://[OWNER]/[PROJECT_NAME]

# generate a key-pair in GitLab with project id
cosign generate-key-pair gitlab://[PROJECT_ID]

CAVEATS:
This command interactively prompts for a password. You can use
the COSIGN_PASSWORD environment variable to provide one.`,
Expand Down
9 changes: 9 additions & 0 deletions doc/cosign_generate-key-pair.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,19 @@ require (
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211004163346-9ae11fe20941
github.com/google/go-github/v39 v39.1.0
github.com/imdario/mergo v0.3.12 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/onsi/gomega v1.16.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.1.0
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.24.0 // indirect
go.opentelemetry.io/proto/otlp v0.9.0 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
k8s.io/klog/v2 v2.20.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -871,8 +871,12 @@ github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211004163346-
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20211004163346-9ae11fe20941/go.mod h1:j3IqhBG3Ox1NXmmhbWU4UmiHVAf2dUgB7le1Ch7JZQ0=
github.com/google/go-github/v27 v27.0.6/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-github/v39 v39.1.0 h1:1vf4gM0D1e+Df2HMxaYC3+o9+Huj3ywGTtWc3VVYaDA=
github.com/google/go-github/v39 v39.1.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
Expand Down Expand Up @@ -1614,6 +1618,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
37 changes: 37 additions & 0 deletions pkg/cosign/git/git.go
Original file line number Diff line number Diff line change
@@ -0,0 +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.ReferenceScheme: github.New(),
gitlab.ReferenceScheme: gitlab.New(),
}

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

func GetProvider(provider string) Git {
return providerMap[provider]
}
119 changes: 119 additions & 0 deletions pkg/cosign/git/github/github.go
Original file line number Diff line number Diff line change
@@ -0,0 +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"
"io"
"io/ioutil"
"net/http"
"os"
"strings"

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

const (
ReferenceScheme = "github"
)

type Gh struct{}

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

func (g *Gh) 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 {
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)

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

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

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

passwordSecretEnv := &github.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 := &github.EncryptedSecret{
Name: "COSIGN_PRIVATE_KEY",
KeyID: key.GetKeyID(),
EncryptedValue: base64.StdEncoding.EncodeToString(keys.PrivateBytes),
}

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

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
}
108 changes: 108 additions & 0 deletions pkg/cosign/git/gitlab/gitlab.go
Original file line number Diff line number Diff line change
@@ -0,0 +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"
"github.com/xanzy/go-gitlab"
)

const (
ReferenceScheme = "gitlab"
)

type Gl struct{}

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

func (g *Gl) 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\"")
}

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

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

_, privateKeyResp, err := client.ProjectVariables.CreateVariable(ref, &gitlab.CreateProjectVariableOptions{
Key: gitlab.String("COSIGN_PRIVATE_KEY"),
Value: gitlab.String(string(keys.PrivateBytes)),
VariableType: gitlab.VariableType(gitlab.EnvVariableType),
Protected: gitlab.Bool(false),
Masked: gitlab.Bool(false),
})
if err != nil {
return errors.Wrap(err, "could not create \"COSIGN_PRIVATE_KEY\" 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\" 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
}
4 changes: 3 additions & 1 deletion pkg/cosign/kubernetes/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import (
"github.com/sigstore/cosign/pkg/cosign"
)

const KeyReference = "k8s://"
const (
KeyReference = "k8s://"
)

func GetKeyPairSecret(ctx context.Context, k8sRef string) (*v1.Secret, error) {
namespace, name, err := parseRef(k8sRef)
Expand Down