...

Source file src/github.com/google/certificate-transparency-go/client/ctclient/cmd/root.go

Documentation: github.com/google/certificate-transparency-go/client/ctclient/cmd

     1  // Copyright 2022 Google LLC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package cmd implements subcommands of ctclient, the command-line utility for
    16  // interacting with CT logs.
    17  package cmd
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"flag"
    23  	"fmt"
    24  	"net/http"
    25  	"os"
    26  	"strings"
    27  	"time"
    28  
    29  	ct "github.com/google/certificate-transparency-go"
    30  	"github.com/google/certificate-transparency-go/client"
    31  	"github.com/google/certificate-transparency-go/jsonclient"
    32  	"github.com/google/certificate-transparency-go/loglist3"
    33  	"github.com/google/certificate-transparency-go/x509util"
    34  	"github.com/spf13/cobra"
    35  	"github.com/spf13/pflag"
    36  	"k8s.io/klog/v2"
    37  )
    38  
    39  const connectionFlags = "{--log_uri uri | --log_name name [--log_list {file|uri}]} [--pub_key file]"
    40  
    41  var (
    42  	skipHTTPSVerify bool
    43  	logName         string
    44  	logList         string
    45  	logURI          string
    46  	pubKey          string
    47  )
    48  
    49  func init() {
    50  	// Add flags added with "flag" package, including klog, to Cobra flag set.
    51  	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
    52  
    53  	flags := rootCmd.PersistentFlags()
    54  	flags.BoolVar(&skipHTTPSVerify, "skip_https_verify", false, "Skip verification of HTTPS transport connection")
    55  	flags.StringVar(&logName, "log_name", "", "Name of log to retrieve information from --log_list for")
    56  	flags.StringVar(&logList, "log_list", loglist3.AllLogListURL, "Location of master log list (URL or filename)")
    57  	flags.StringVar(&logURI, "log_uri", "https://ct.googleapis.com/rocketeer", "CT log base URI")
    58  	flags.StringVar(&pubKey, "pub_key", "", "Name of file containing log's public key")
    59  }
    60  
    61  // rootCmd represents the base command when called without any subcommands.
    62  var rootCmd = &cobra.Command{
    63  	Use:   "ctclient",
    64  	Short: "A command line client for Certificate Transparency logs",
    65  
    66  	PersistentPreRun: func(cmd *cobra.Command, _ []string) {
    67  		flag.Parse()
    68  	},
    69  }
    70  
    71  // Execute adds all child commands to the root command and sets flags
    72  // appropriately. It needs to be called exactly once by main().
    73  func Execute() {
    74  	if err := rootCmd.Execute(); err != nil {
    75  		klog.Fatal(err)
    76  	}
    77  }
    78  
    79  func signatureToString(signed *ct.DigitallySigned) string {
    80  	return fmt.Sprintf("Signature: Hash=%v Sign=%v Value=%x", signed.Algorithm.Hash, signed.Algorithm.Signature, signed.Signature)
    81  }
    82  
    83  func exitWithDetails(err error) {
    84  	if err, ok := err.(client.RspError); ok {
    85  		klog.Infof("HTTP details: status=%d, body:\n%s", err.StatusCode, err.Body)
    86  	}
    87  	klog.Exit(err.Error())
    88  }
    89  
    90  func connect(ctx context.Context) *client.LogClient {
    91  	var tlsCfg *tls.Config
    92  	if skipHTTPSVerify {
    93  		klog.Warning("Skipping HTTPS connection verification")
    94  		tlsCfg = &tls.Config{InsecureSkipVerify: skipHTTPSVerify}
    95  	}
    96  	httpClient := &http.Client{
    97  		Timeout: 10 * time.Second,
    98  		Transport: &http.Transport{
    99  			TLSHandshakeTimeout:   30 * time.Second,
   100  			ResponseHeaderTimeout: 30 * time.Second,
   101  			MaxIdleConnsPerHost:   10,
   102  			DisableKeepAlives:     false,
   103  			MaxIdleConns:          100,
   104  			IdleConnTimeout:       90 * time.Second,
   105  			ExpectContinueTimeout: 1 * time.Second,
   106  			TLSClientConfig:       tlsCfg,
   107  		},
   108  	}
   109  	opts := jsonclient.Options{UserAgent: "ct-go-ctclient/1.0"}
   110  	if pubKey != "" {
   111  		pubkey, err := os.ReadFile(pubKey)
   112  		if err != nil {
   113  			klog.Exit(err)
   114  		}
   115  		opts.PublicKey = string(pubkey)
   116  	}
   117  
   118  	uri := logURI
   119  	if logName != "" {
   120  		llData, err := x509util.ReadFileOrURL(logList, httpClient)
   121  		if err != nil {
   122  			klog.Exitf("Failed to read log list: %v", err)
   123  		}
   124  		ll, err := loglist3.NewFromJSON(llData)
   125  		if err != nil {
   126  			klog.Exitf("Failed to build log list: %v", err)
   127  		}
   128  
   129  		logs := ll.FindLogByName(logName)
   130  		if len(logs) == 0 {
   131  			klog.Exitf("No log with name like %q found in loglist %q", logName, logList)
   132  		}
   133  		if len(logs) > 1 {
   134  			logNames := make([]string, len(logs))
   135  			for i, log := range logs {
   136  				logNames[i] = fmt.Sprintf("%q", log.Description)
   137  			}
   138  			klog.Exitf("Multiple logs with name like %q found in loglist: %s", logName, strings.Join(logNames, ","))
   139  		}
   140  		uri = logs[0].URL
   141  		if opts.PublicKey == "" {
   142  			opts.PublicKeyDER = logs[0].Key
   143  		}
   144  	}
   145  
   146  	klog.V(1).Infof("Use CT log at %s", uri)
   147  	logClient, err := client.New(uri, httpClient, opts)
   148  	if err != nil {
   149  		klog.Exit(err)
   150  	}
   151  
   152  	return logClient
   153  }
   154  

View as plain text