1
0
mirror of synced 2026-10-24 12:59:22 +03:00

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

* feat: add github support to generate-key-pair command
* feat: gitlab integration
* feat: add github/gitlab to generate-key-pair command's description

Signed-off-by: Erkan Zileli <erkan.zileli@trendyol.com>
Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>
Co-authored-with: Batuhan Apaydın <batuhan.apaydin@trendyol.com>
Co-authored-with: Furkan Türkal <furkan.turkal@trendyol.com>
This commit is contained in:
Erkan Zileli
2021-10-12 00:26:04 +03:00
committed by GitHub
parent 8a67024298
commit 5302c87ab9
9 changed files with 314 additions and 2 deletions

View File

@@ -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"
@@ -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]
switch provider {
case "k8s":
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)

View File

@@ -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.`,

View File

@@ -33,6 +33,15 @@ cosign generate-key-pair [flags]
# 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.

3
go.mod
View File

@@ -38,6 +38,7 @@ require (
github.com/envoyproxy/protoc-gen-validate v0.6.1 // indirect
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // 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
@@ -46,9 +47,11 @@ require (
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

5
go.sum
View File

@@ -896,8 +896,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=
@@ -1655,6 +1659,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=

37
pkg/cosign/git/git.go Normal file
View File

@@ -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]
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)