github.com/containerd/nerdctl@v1.7.7/pkg/signutil/notationutil.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package signutil
    18  
    19  import (
    20  	"bufio"
    21  	"context"
    22  	"os"
    23  	"os/exec"
    24  	"strings"
    25  
    26  	"github.com/containerd/log"
    27  	"github.com/containerd/nerdctl/pkg/imgutil"
    28  )
    29  
    30  // SignNotation signs an image(`rawRef`) using a notation key name (`keyNameRef`)
    31  func SignNotation(rawRef string, keyNameRef string) error {
    32  	notationExecutable, err := exec.LookPath("notation")
    33  	if err != nil {
    34  		log.L.WithError(err).Error("notation executable not found in path $PATH")
    35  		log.L.Info("you might consider installing notation from: https://notaryproject.dev/docs/installation/cli/")
    36  		return err
    37  	}
    38  
    39  	notationCmd := exec.Command(notationExecutable, []string{"sign"}...)
    40  	notationCmd.Env = os.Environ()
    41  
    42  	// If keyNameRef is empty, don't append --key to notation command. This will cause using the notation default key.
    43  	if keyNameRef != "" {
    44  		notationCmd.Args = append(notationCmd.Args, "--key", keyNameRef)
    45  	}
    46  
    47  	notationCmd.Args = append(notationCmd.Args, rawRef)
    48  
    49  	log.L.Debugf("running %s %v", notationExecutable, notationCmd.Args)
    50  
    51  	err = processNotationIO(notationCmd)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	return notationCmd.Wait()
    57  }
    58  
    59  // VerifyNotation verifies an image(`rawRef`) with the pre-configured notation trust policy
    60  // `hostsDirs` are used to resolve image `rawRef`
    61  func VerifyNotation(ctx context.Context, rawRef string, hostsDirs []string) (string, error) {
    62  	digest, err := imgutil.ResolveDigest(ctx, rawRef, false, hostsDirs)
    63  	if err != nil {
    64  		log.G(ctx).WithError(err).Errorf("unable to resolve digest for an image %s: %v", rawRef, err)
    65  		return rawRef, err
    66  	}
    67  	ref := rawRef
    68  	if !strings.Contains(ref, "@") {
    69  		ref += "@" + digest
    70  	}
    71  
    72  	log.G(ctx).Debugf("verifying image: %s", ref)
    73  
    74  	notationExecutable, err := exec.LookPath("notation")
    75  	if err != nil {
    76  		log.G(ctx).WithError(err).Error("notation executable not found in path $PATH")
    77  		log.G(ctx).Info("you might consider installing notation from: https://notaryproject.dev/docs/installation/cli/")
    78  		return ref, err
    79  	}
    80  
    81  	notationCmd := exec.Command(notationExecutable, []string{"verify"}...)
    82  	notationCmd.Env = os.Environ()
    83  
    84  	notationCmd.Args = append(notationCmd.Args, ref)
    85  
    86  	log.G(ctx).Debugf("running %s %v", notationExecutable, notationCmd.Args)
    87  
    88  	err = processNotationIO(notationCmd)
    89  	if err != nil {
    90  		return ref, err
    91  	}
    92  	if err := notationCmd.Wait(); err != nil {
    93  		return ref, err
    94  	}
    95  
    96  	return ref, nil
    97  }
    98  
    99  func processNotationIO(notationCmd *exec.Cmd) error {
   100  	stdout, err := notationCmd.StdoutPipe()
   101  	if err != nil {
   102  		log.L.Warn("notation: " + err.Error())
   103  	}
   104  	stderr, err := notationCmd.StderrPipe()
   105  	if err != nil {
   106  		log.L.Warn("notation: " + err.Error())
   107  	}
   108  	if err := notationCmd.Start(); err != nil {
   109  		// only return err if it's critical (notation start failed.)
   110  		return err
   111  	}
   112  
   113  	scanner := bufio.NewScanner(stdout)
   114  	for scanner.Scan() {
   115  		log.L.Info("notation: " + scanner.Text())
   116  	}
   117  	if err := scanner.Err(); err != nil {
   118  		log.L.Warn("notation: " + err.Error())
   119  	}
   120  
   121  	errScanner := bufio.NewScanner(stderr)
   122  	for errScanner.Scan() {
   123  		log.L.Info("notation: " + errScanner.Text())
   124  	}
   125  	if err := errScanner.Err(); err != nil {
   126  		log.L.Warn("notation: " + err.Error())
   127  	}
   128  
   129  	return nil
   130  }