Skip to content

Commit

Permalink
add krel sign blobs and images commands
Browse files Browse the repository at this point in the history
Signed-off-by: cpanato <ctadeu@gmail.com>
  • Loading branch information
cpanato committed Nov 29, 2022
1 parent d6d5fc0 commit d2e9c1f
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 6 deletions.
76 changes: 76 additions & 0 deletions cmd/krel/cmd/sign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright 2022 The Kubernetes 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 cmd

import (
"time"

"github.com/spf13/cobra"
)

const (
verboseFlag = "verbose"
timeoutFlag = "timeout"
maxWorkersFlag = "max-workers"
)

type signOptions struct {
verbose bool
timeout time.Duration
// The amount of maximum workers for parallel executions.
// Defaults to 100.
maxWorkers uint
}

var singOpts = &signOptions{}

// signCmd represents the subcommand for `krel sign`
var signCmd = &cobra.Command{
Use: "sign",
Short: "sign images and blobs",
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
return cmd.Help()
},
}

func init() {
signCmd.PersistentFlags().BoolVar(
&singOpts.verbose,
verboseFlag,
false,
"can be used to enable a higher log verbosity",
)

signCmd.PersistentFlags().DurationVarP(
&singOpts.timeout,
timeoutFlag,
"t",
3*time.Minute,
"is the default timeout for network operations",
)

signCmd.PersistentFlags().UintVar(
&singOpts.maxWorkers,
maxWorkersFlag,
100,
"The amount of maximum workers for parallel executions",
)

rootCmd.AddCommand(signCmd)
}
227 changes: 227 additions & 0 deletions cmd/krel/cmd/sign_blobs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
Copyright 2022 The Kubernetes 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 cmd

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/nozzle/throttler"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"sigs.k8s.io/release-sdk/gcli"
"sigs.k8s.io/release-sdk/object"
"sigs.k8s.io/release-sdk/sign"
)

const (
outputPathFlag = "output-path"
privateKeyPathFlag = "private-key-path"
publicKeyPathFlag = "public-key-path"
)

type signBlobOptions struct {
outputPath string

privateKeyPath string
publicKeyPath string
}

type signingBundle struct {
destinationPathToCopy string
fileToSign string
fileLocalLocation string
}

var signBlobOpts = &signBlobOptions{}

// signBlobCmd represents the subcommand for `krel sign blobs`
var signBlobCmd = &cobra.Command{
Use: "blobs",
Short: "Sign blobs",
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runSignBlobs(singOpts, signBlobOpts, args)
},
}

func init() {
signBlobCmd.PersistentFlags().StringVarP(
&signBlobOpts.outputPath,
outputPathFlag,
"",
"",
"write the certificate and signatures to a file in the set path",
)

signBlobCmd.PersistentFlags().StringVarP(
&signBlobOpts.privateKeyPath,
privateKeyPathFlag,
"",
"",
"path for the cosign private key",
)

signBlobCmd.PersistentFlags().StringVarP(
&signBlobOpts.publicKeyPath,
publicKeyPathFlag,
"",
"",
"path for the cosign public key",
)

signCmd.AddCommand(signBlobCmd)
}

func runSignBlobs(signOpts *signOptions, signBlobOpts *signBlobOptions, args []string) error {
err := validateSignBlobsArgs(args)
if err != nil {
return fmt.Errorf("blobs to be signed does not exist: %w", err)
}

var bundle []signingBundle
isGCSBucket := false
if strings.HasPrefix(args[0], object.GcsPrefix) {
// GCS Bucket remote location
isGCSBucket = true

tempDir, err := os.MkdirTemp("", "release-sign-blobs-")
if err != nil {
return fmt.Errorf("creating a temporary directory to save the files to be signed: %w", err)
}

logrus.Infof("Getting a list of files to be signed from %s", args[0])
output, err := gcli.GSUtilOutput("ls", "-R", args[0])
if err != nil {
return fmt.Errorf("listing bucket contents: %w", err)
}

gcsClient := object.NewGCS()
for _, file := range strings.Fields(output) {
if strings.HasSuffix(file, ".sha256") || strings.HasSuffix(file, ".sha512") ||
strings.HasSuffix(file, ":") || strings.HasSuffix(file, ".docker_tag") ||
strings.Contains(file, "SHA256SUMS") || strings.Contains(file, "README") ||
strings.Contains(file, "Makefile") {
continue
}
temp := strings.TrimPrefix(file, object.GcsPrefix)
err = gcsClient.CopyToLocal(file, tempDir)
if err != nil {
return fmt.Errorf("copying file to sign: %w", err)
}

bundle = append(bundle, signingBundle{
destinationPathToCopy: filepath.Dir(temp),
fileToSign: filepath.Base(temp),
fileLocalLocation: fmt.Sprintf("%s/%s", tempDir, filepath.Base(temp)),
})
}
} else {
// Local files
for _, arg := range args {
bundle = append(bundle, signingBundle{
fileLocalLocation: arg,
fileToSign: filepath.Base(arg),
})
}
}

t := throttler.New(int(signOpts.maxWorkers), len(args))
for _, fileBundle := range bundle {
go func(fileBundle signingBundle) {
logrus.Infof("Signing %s...", fileBundle.fileToSign)
signerOpts := sign.Default()
signerOpts.Verbose = signOpts.verbose
signerOpts.Timeout = signOpts.timeout
signerOpts.PrivateKeyPath = signBlobOpts.privateKeyPath
signerOpts.PublicKeyPath = signBlobOpts.publicKeyPath

signerOpts.OutputCertificatePath = fmt.Sprintf("%s/%s.cert", signBlobOpts.outputPath, fileBundle.fileToSign)
signerOpts.OutputSignaturePath = fmt.Sprintf("%s/%s.sig", signBlobOpts.outputPath, fileBundle.fileToSign)
if signBlobOpts.outputPath == "" {
signerOpts.OutputCertificatePath = fmt.Sprintf("%s.cert", fileBundle.fileLocalLocation)
signerOpts.OutputSignaturePath = fmt.Sprintf("%s.sig", fileBundle.fileLocalLocation)
}

signer := sign.New(signerOpts)
_, err := signer.SignFile(fileBundle.fileLocalLocation)
if err != nil {
t.Done(fmt.Errorf("signing the file %s: %w", fileBundle.fileLocalLocation, err))
return
}
t.Done(nil)
}(fileBundle)

if t.Throttle() > 0 {
break
}
}
if err := t.Err(); err != nil {
return fmt.Errorf("signing the blobs: %w", err)
}

if isGCSBucket {
logrus.Info("Copying Certificates and Signatures back to the bucket...")
for _, fileBundle := range bundle {
certFiles := fmt.Sprintf("%s/%s.cert", signBlobOpts.outputPath, fileBundle.fileToSign)
signFiles := fmt.Sprintf("%s/%s.sig", signBlobOpts.outputPath, fileBundle.fileToSign)
if signBlobOpts.outputPath == "" {
certFiles = fmt.Sprintf("%s.cert", fileBundle.fileLocalLocation)
signFiles = fmt.Sprintf("%s.sig", fileBundle.fileLocalLocation)
}

logrus.Infof("Copying %s and %s...", certFiles, signFiles)
_, err = gcli.GSUtilOutput("cp", "-n", certFiles, signFiles, fmt.Sprintf("%s%s", object.GcsPrefix, fileBundle.destinationPathToCopy))
if err != nil {
return fmt.Errorf("copying certificates and signatures to the bucket: %w", err)
}
}
}

logrus.Info("Done")
return nil
}

func validateSignBlobsArgs(args []string) error {
if len(args) < 1 {
return errors.New("missing set files or gcs bucket")
}

if len(args) > 1 {
tempArgs := strings.Join(args, ",")
if strings.Count(tempArgs, object.GcsPrefix) > 0 {
return errors.New("only one GCS Bucket is allowed and/or cannot mix with local files")
}
}

if strings.HasPrefix(args[0], object.GcsPrefix) {
return nil
}

for _, file := range args {
if _, err := os.Stat(file); os.IsNotExist(err) {
return fmt.Errorf("blob %s does not exist", file)
}
}

return nil
}
43 changes: 43 additions & 0 deletions cmd/krel/cmd/sign_images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Copyright 2022 The Kubernetes 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 cmd

import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

// signImagesCmd represents the subcommand for `krel sign images`
var signImagesCmd = &cobra.Command{
Use: "images",
Short: "Sign images",
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runSignImages(singOpts, args)
},
}

func init() {
signCmd.AddCommand(signImagesCmd)
}

func runSignImages(signOpts *signOptions, args []string) error { //nolint:unparam // TODO: implement me :)
logrus.Info("Not implemented")

return nil
}
19 changes: 19 additions & 0 deletions gcb/release/cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ steps:
- "./compile-release-tools"
- "krel"

- name: gcr.io/k8s-staging-releng/k8s-cloud-builder:${_KUBE_CROSS_VERSION}
dir: "/workspace"
env:
- "TOOL_ORG=${_TOOL_ORG}"
- "TOOL_REPO=${_TOOL_REPO}"
- "TOOL_REF=${_TOOL_REF}"
- "K8S_ORG=${_K8S_ORG}"
- "K8S_REPO=${_K8S_REPO}"
- "K8S_REF=${_K8S_REF}"
- GOOGLE_SERVICE_ACCOUNT_NAME=krel-staging@k8s-releng-prod.iam.gserviceaccount.com
args:
- "bin/krel"
- "sign""
- "blobs"
- "${_KUBERNETES_GCS_BUCKET}"
- "--log-level=${_LOG_LEVEL}"


- name: gcr.io/k8s-staging-releng/k8s-cloud-builder:${_KUBE_CROSS_VERSION}
dir: "/workspace"
env:
Expand Down Expand Up @@ -75,6 +93,7 @@ tags:
- ${_MINOR_VERSION_TAG}
- ${_PATCH_VERSION_TAG}
- ${_KUBERNETES_VERSION_TAG}
- ${_KUBERNETES_GCS_BUCKET}

options:
machineType: N1_HIGHCPU_32
Expand Down
Loading

0 comments on commit d2e9c1f

Please sign in to comment.