...

Source file src/github.com/sigstore/rekor/pkg/api/api.go

Documentation: github.com/sigstore/rekor/pkg/api

     1  //
     2  // Copyright 2021 The Sigstore 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  package api
    17  
    18  import (
    19  	"context"
    20  	"crypto/sha256"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"encoding/hex"
    24  	"fmt"
    25  	"time"
    26  
    27  	"github.com/google/trillian"
    28  	"github.com/redis/go-redis/v9"
    29  	"github.com/spf13/viper"
    30  	"golang.org/x/exp/slices"
    31  	"google.golang.org/grpc"
    32  	"google.golang.org/grpc/credentials/insecure"
    33  
    34  	"github.com/sigstore/rekor/pkg/indexstorage"
    35  	"github.com/sigstore/rekor/pkg/log"
    36  	"github.com/sigstore/rekor/pkg/pubsub"
    37  	"github.com/sigstore/rekor/pkg/sharding"
    38  	"github.com/sigstore/rekor/pkg/signer"
    39  	"github.com/sigstore/rekor/pkg/storage"
    40  	"github.com/sigstore/rekor/pkg/trillianclient"
    41  	"github.com/sigstore/rekor/pkg/witness"
    42  	"github.com/sigstore/sigstore/pkg/cryptoutils"
    43  	"github.com/sigstore/sigstore/pkg/signature"
    44  	"github.com/sigstore/sigstore/pkg/signature/options"
    45  
    46  	_ "github.com/sigstore/rekor/pkg/pubsub/gcp" // Load GCP pubsub implementation
    47  )
    48  
    49  func dial(ctx context.Context, rpcServer string) (*grpc.ClientConn, error) {
    50  	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    51  	defer cancel()
    52  
    53  	// Set up and test connection to rpc server
    54  	creds := insecure.NewCredentials()
    55  	conn, err := grpc.DialContext(ctx, rpcServer, grpc.WithTransportCredentials(creds))
    56  	if err != nil {
    57  		log.Logger.Fatalf("Failed to connect to RPC server:", err)
    58  	}
    59  	return conn, nil
    60  }
    61  
    62  type API struct {
    63  	logClient  trillian.TrillianLogClient
    64  	logID      int64
    65  	logRanges  sharding.LogRanges
    66  	pubkey     string // PEM encoded public key
    67  	pubkeyHash string // SHA256 hash of DER-encoded public key
    68  	signer     signature.Signer
    69  	// stops checkpoint publishing
    70  	checkpointPublishCancel context.CancelFunc
    71  	// Publishes notifications when new entries are added to the log. May be
    72  	// nil if no publisher is configured.
    73  	newEntryPublisher pubsub.Publisher
    74  }
    75  
    76  func NewAPI(treeID uint) (*API, error) {
    77  	logRPCServer := fmt.Sprintf("%s:%d",
    78  		viper.GetString("trillian_log_server.address"),
    79  		viper.GetUint("trillian_log_server.port"))
    80  	ctx := context.Background()
    81  	tConn, err := dial(ctx, logRPCServer)
    82  	if err != nil {
    83  		return nil, fmt.Errorf("dial: %w", err)
    84  	}
    85  	logAdminClient := trillian.NewTrillianAdminClient(tConn)
    86  	logClient := trillian.NewTrillianLogClient(tConn)
    87  
    88  	shardingConfig := viper.GetString("trillian_log_server.sharding_config")
    89  	ranges, err := sharding.NewLogRanges(ctx, logClient, shardingConfig, treeID)
    90  	if err != nil {
    91  		return nil, fmt.Errorf("unable get sharding details from sharding config: %w", err)
    92  	}
    93  
    94  	tid := int64(treeID)
    95  	if tid == 0 {
    96  		log.Logger.Info("No tree ID specified, attempting to create a new tree")
    97  		t, err := trillianclient.CreateAndInitTree(ctx, logAdminClient, logClient)
    98  		if err != nil {
    99  			return nil, fmt.Errorf("create and init tree: %w", err)
   100  		}
   101  		tid = t.TreeId
   102  	}
   103  	log.Logger.Infof("Starting Rekor server with active tree %v", tid)
   104  	ranges.SetActive(tid)
   105  
   106  	rekorSigner, err := signer.New(ctx, viper.GetString("rekor_server.signer"),
   107  		viper.GetString("rekor_server.signer-passwd"))
   108  	if err != nil {
   109  		return nil, fmt.Errorf("getting new signer: %w", err)
   110  	}
   111  	pk, err := rekorSigner.PublicKey(options.WithContext(ctx))
   112  	if err != nil {
   113  		return nil, fmt.Errorf("getting public key: %w", err)
   114  	}
   115  	b, err := x509.MarshalPKIXPublicKey(pk)
   116  	if err != nil {
   117  		return nil, fmt.Errorf("marshalling public key: %w", err)
   118  	}
   119  	pubkeyHashBytes := sha256.Sum256(b)
   120  
   121  	pubkey := cryptoutils.PEMEncode(cryptoutils.PublicKeyPEMType, b)
   122  
   123  	var newEntryPublisher pubsub.Publisher
   124  	if p := viper.GetString("rekor_server.new_entry_publisher"); p != "" {
   125  		if !viper.GetBool("rekor_server.publish_events_protobuf") && !viper.GetBool("rekor_server.publish_events_json") {
   126  			return nil, fmt.Errorf("%q is configured but neither %q or %q are enabled", "new_entry_publisher", "publish_events_protobuf", "publish_events_json")
   127  		}
   128  		newEntryPublisher, err = pubsub.Get(ctx, p)
   129  		if err != nil {
   130  			return nil, fmt.Errorf("init event publisher: %w", err)
   131  		}
   132  		log.ContextLogger(ctx).Infof("Initialized new entry event publisher: %s", p)
   133  	}
   134  
   135  	return &API{
   136  		// Transparency Log Stuff
   137  		logClient: logClient,
   138  		logID:     tid,
   139  		logRanges: ranges,
   140  		// Signing/verifying fields
   141  		pubkey:     string(pubkey),
   142  		pubkeyHash: hex.EncodeToString(pubkeyHashBytes[:]),
   143  		signer:     rekorSigner,
   144  		// Utility functionality not required for operation of the core service
   145  		newEntryPublisher: newEntryPublisher,
   146  	}, nil
   147  }
   148  
   149  var (
   150  	api                      *API
   151  	attestationStorageClient storage.AttestationStorage
   152  	indexStorageClient       indexstorage.IndexStorage
   153  	redisClient              *redis.Client
   154  )
   155  
   156  func ConfigureAPI(treeID uint) {
   157  	var err error
   158  
   159  	api, err = NewAPI(treeID)
   160  	if err != nil {
   161  		log.Logger.Panic(err)
   162  	}
   163  	if viper.GetBool("enable_retrieve_api") || viper.GetBool("enable_stable_checkpoint") ||
   164  		slices.Contains(viper.GetStringSlice("enabled_api_endpoints"), "searchIndex") {
   165  		indexStorageClient, err = indexstorage.NewIndexStorage(viper.GetString("search_index.storage_provider"))
   166  		if err != nil {
   167  			log.Logger.Panic(err)
   168  		}
   169  	}
   170  
   171  	if viper.GetBool("enable_attestation_storage") {
   172  		attestationStorageClient, err = storage.NewAttestationStorage()
   173  		if err != nil {
   174  			log.Logger.Panic(err)
   175  		}
   176  	}
   177  
   178  	if viper.GetBool("enable_stable_checkpoint") {
   179  		redisClient = NewRedisClient()
   180  		checkpointPublisher := witness.NewCheckpointPublisher(context.Background(), api.logClient, api.logRanges.ActiveTreeID(),
   181  			viper.GetString("rekor_server.hostname"), api.signer, redisClient, viper.GetUint("publish_frequency"), CheckpointPublishCount)
   182  
   183  		// create context to cancel goroutine on server shutdown
   184  		ctx, cancel := context.WithCancel(context.Background())
   185  		api.checkpointPublishCancel = cancel
   186  		checkpointPublisher.StartPublisher(ctx)
   187  	}
   188  }
   189  
   190  func NewRedisClient() *redis.Client {
   191  
   192  	opts := &redis.Options{
   193  		Addr:     fmt.Sprintf("%v:%v", viper.GetString("redis_server.address"), viper.GetUint64("redis_server.port")),
   194  		Password: viper.GetString("redis_server.password"),
   195  		Network:  "tcp",
   196  		DB:       0, // default DB
   197  	}
   198  
   199  	// #nosec G402
   200  	if viper.GetBool("redis_server.enable-tls") {
   201  		opts.TLSConfig = &tls.Config{
   202  			InsecureSkipVerify: viper.GetBool("redis_server.insecure-skip-verify"), //nolint: gosec
   203  		}
   204  	}
   205  
   206  	return redis.NewClient(opts)
   207  }
   208  
   209  func StopAPI() {
   210  	api.checkpointPublishCancel()
   211  
   212  	if api.newEntryPublisher != nil {
   213  		if err := api.newEntryPublisher.Close(); err != nil {
   214  			log.Logger.Errorf("shutting down newEntryPublisher: %v", err)
   215  		}
   216  	}
   217  
   218  	if indexStorageClient != nil {
   219  		if err := indexStorageClient.Shutdown(); err != nil {
   220  			log.Logger.Errorf("shutting down indexStorageClient: %v", err)
   221  		}
   222  	}
   223  }
   224  

View as plain text