Skip to content

Commit

Permalink
Merge "FAB-6128 use docker upload for TLS materials"
Browse files Browse the repository at this point in the history
  • Loading branch information
C0rWin authored and Gerrit Code Review committed Sep 17, 2017
2 parents babf82e + ef27e65 commit 22118e4
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 40 deletions.
39 changes: 29 additions & 10 deletions core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"io"
"path/filepath"
"reflect"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -56,6 +57,10 @@ const (

//HistoryQueryExecutorKey is used to attach ledger history query executor context
HistoryQueryExecutorKey key = "historyqueryexecutorkey"

// Mutual TLS auth client key and cert paths in the chaincode container
TLSClientKeyPath string = "/etc/hyperledger/fabric/client.key"
TLSClientCertPath string = "/etc/hyperledger/fabric/client.crt"
)

//this is basically the singleton that supports the
Expand Down Expand Up @@ -356,15 +361,20 @@ func (chaincodeSupport *ChaincodeSupport) sendReady(context context.Context, ccc
return err
}

func (chaincodeSupport *ChaincodeSupport) appendTLScerts(args []string, keyPair *accesscontrol.CertAndPrivKeyPair) []string {
// returns a map of file path <-> []byte for all files related to TLS
func (chaincodeSupport *ChaincodeSupport) getTLSFiles(keyPair *accesscontrol.CertAndPrivKeyPair) map[string][]byte {
if keyPair == nil {
return args
return nil
}

return map[string][]byte{
TLSClientKeyPath: []byte(keyPair.Key),
TLSClientCertPath: []byte(keyPair.Cert),
}
return append(args, []string{"--key", keyPair.Key, "--cert", keyPair.Cert}...)
}

//get args and env given chaincodeID
func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cccid *ccprovider.CCContext, cLang pb.ChaincodeSpec_Type) (args []string, envs []string, err error) {
func (chaincodeSupport *ChaincodeSupport) getLaunchConfigs(cccid *ccprovider.CCContext, cLang pb.ChaincodeSpec_Type) (args []string, envs []string, filesToUpload map[string][]byte, err error) {
canName := cccid.GetCanonicalName()
envs = []string{"CORE_CHAINCODE_ID_NAME=" + canName}

Expand All @@ -382,12 +392,14 @@ func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cccid *ccprovider.CCCont
if chaincodeSupport.peerTLS {
certKeyPair, err = chaincodeSupport.auth.Generate(cccid.GetCanonicalName())
if err != nil {
return nil, nil, errors.WithMessage(err, fmt.Sprintf("failed generating TLS cert for %s", cccid.GetCanonicalName()))
return nil, nil, nil, errors.WithMessage(err, fmt.Sprintf("failed generating TLS cert for %s", cccid.GetCanonicalName()))
}
envs = append(envs, "CORE_PEER_TLS_ENABLED=true")
if chaincodeSupport.peerTLSSvrHostOrd != "" {
envs = append(envs, "CORE_PEER_TLS_SERVERHOSTOVERRIDE="+chaincodeSupport.peerTLSSvrHostOrd)
}
envs = append(envs, fmt.Sprintf("CORE_TLS_CLIENT_KEY_PATH=%s", TLSClientKeyPath))
envs = append(envs, fmt.Sprintf("CORE_TLS_CLIENT_CERT_PATH=%s", TLSClientCertPath))
} else {
envs = append(envs, "CORE_PEER_TLS_ENABLED=false")
}
Expand All @@ -406,17 +418,23 @@ func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cccid *ccprovider.CCCont
switch cLang {
case pb.ChaincodeSpec_GOLANG, pb.ChaincodeSpec_CAR:
args = []string{"chaincode", fmt.Sprintf("-peer.address=%s", chaincodeSupport.peerAddress)}
args = theChaincodeSupport.appendTLScerts(args, certKeyPair)
case pb.ChaincodeSpec_JAVA:
args = []string{"java", "-jar", "chaincode.jar", "--peerAddress", chaincodeSupport.peerAddress}
case pb.ChaincodeSpec_NODE:
args = []string{"/bin/sh", "-c", fmt.Sprintf("cd /usr/local/src; node chaincode.js --peer.address %s", chaincodeSupport.peerAddress)}

default:
return nil, nil, errors.Errorf("unknown chaincodeType: %s", cLang)
return nil, nil, nil, errors.Errorf("unknown chaincodeType: %s", cLang)
}

filesToUpload = theChaincodeSupport.getTLSFiles(certKeyPair)

chaincodeLogger.Debugf("Executable is %s", args[0])
chaincodeLogger.Debugf("Args %v", args)
return args, envs, nil
chaincodeLogger.Debugf("Envs %v", envs)
chaincodeLogger.Debugf("FilesToUpload %v", reflect.ValueOf(filesToUpload).MapKeys())

return args, envs, filesToUpload, nil
}

//launchAndWaitForRegister will launch container if not already running. Use
Expand Down Expand Up @@ -467,7 +485,7 @@ func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.

//launch the chaincode

args, env, err := chaincodeSupport.getArgsAndEnv(cccid, cLang)
args, env, filesToUpload, err := chaincodeSupport.getLaunchConfigs(cccid, cLang)
if err != nil {
return err
}
Expand All @@ -487,7 +505,8 @@ func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.
return nil
}

sir := container.StartImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, Version: cccid.Version}, Builder: builder, Args: args, Env: env, PrelaunchFunc: preLaunchFunc}
ccid := ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, Version: cccid.Version}
sir := container.StartImageReq{CCID: ccid, Builder: builder, Args: args, Env: env, FilesToUpload: filesToUpload, PrelaunchFunc: preLaunchFunc}

ipcCtxt := context.WithValue(ctxt, ccintf.GetCCHandlerKey(), chaincodeSupport)

Expand Down
42 changes: 31 additions & 11 deletions core/chaincode/chaincode_support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,33 +639,53 @@ func getHistory(t *testing.T, chainID, ccname string, ccSide *mockpeer.MockCCCom
return nil
}

func getArgsAndEnv(t *testing.T, auth accesscontrol.Authenticator) {
func getLaunchConfigs(t *testing.T, auth accesscontrol.Authenticator) {
newCCSupport := &ChaincodeSupport{peerTLS: true, chaincodeLogLevel: "debug", shimLogLevel: "info"}

//set the authenticator for generating TLS stuff
newCCSupport.auth = auth

ccContext := ccprovider.NewCCContext("dummyChannelId", "mycc", "v0", "dummyTxid", false, nil, nil)
args, envs, err := newCCSupport.getArgsAndEnv(ccContext, pb.ChaincodeSpec_NODE)
args, envs, filesToUpload, err := newCCSupport.getLaunchConfigs(ccContext, pb.ChaincodeSpec_GOLANG)
if err != nil {
t.Fatalf("calling getArgsAndEnv() failed with error %s", err)
t.Fatalf("calling getLaunchConfigs() failed with error %s", err)
}

if len(args) != 2 {
t.Fatalf("calling getLaunchConfigs() for golang chaincode should have returned an array of 2 elements for Args, but got %v", args)
}
if args[0] != "chaincode" || !strings.HasPrefix(args[1], "-peer.address") {
t.Fatalf("calling getLaunchConfigs() should have returned the start command for golang chaincode, but got %v", args)
}
if len(envs) != 6 {
t.Fatalf("calling getLaunchConfigs() with TLS enabled should have returned an array of 6 elements for Envs, but got %v", envs)
}
if envs[0] != "CORE_CHAINCODE_ID_NAME=mycc:v0" || envs[1] != "CORE_PEER_TLS_ENABLED=true" ||
envs[2] != "CORE_TLS_CLIENT_KEY_PATH=/etc/hyperledger/fabric/client.key" || envs[3] != "CORE_TLS_CLIENT_CERT_PATH=/etc/hyperledger/fabric/client.crt" ||
envs[4] != "CORE_CHAINCODE_LOGGING_LEVEL=debug" || envs[5] != "CORE_CHAINCODE_LOGGING_SHIM=info" {
t.Fatalf("calling getLaunchConfigs() with TLS enabled should have returned the proper environment variables, but got %v", envs)
}
if len(filesToUpload) != 2 {
t.Fatalf("calling getLaunchConfigs() with TLS enabled should have returned an array of 2 elements for filesToUpload, but got %v", len(filesToUpload))
}

args, envs, _, err = newCCSupport.getLaunchConfigs(ccContext, pb.ChaincodeSpec_NODE)
if len(args) != 3 {
t.Fatalf("calling getArgsAndEnv() should have returned an array of 3 elements for Args, but got %v", args)
t.Fatalf("calling getLaunchConfigs() for node chaincode should have returned an array of 3 elements for Args, but got %v", args)
}

if args[0] != "/bin/sh" || args[1] != "-c" && !strings.HasPrefix(args[2], "cd /usr/local/src; node chaincode.js --peer.address") {
t.Fatalf("calling getArgsAndEnv() should have returned the start command for node.js chaincode, but got %v", args)
if args[0] != "/bin/sh" || args[1] != "-c" || !strings.HasPrefix(args[2], "cd /usr/local/src; node chaincode.js --peer.address") {
t.Fatalf("calling getLaunchConfigs() should have returned the start command for node.js chaincode, but got %v", args)
}

newCCSupport.peerTLS = false
args, envs, _, err = newCCSupport.getLaunchConfigs(ccContext, pb.ChaincodeSpec_GOLANG)
if len(envs) != 4 {
t.Fatalf("calling getArgsAndEnv() should have returned an array of 4 elements for Envs, but got %v", envs)
t.Fatalf("calling getLaunchConfigs() with TLS disabled should have returned an array of 4 elements for Envs, but got %v", envs)
}

if envs[0] != "CORE_CHAINCODE_ID_NAME=mycc:v0" || envs[1] != "CORE_PEER_TLS_ENABLED=true" ||
if envs[0] != "CORE_CHAINCODE_ID_NAME=mycc:v0" || envs[1] != "CORE_PEER_TLS_ENABLED=false" ||
envs[2] != "CORE_CHAINCODE_LOGGING_LEVEL=debug" || envs[3] != "CORE_CHAINCODE_LOGGING_SHIM=info" {
t.Fatalf("calling getArgsAndEnv() should have returned the proper environment variables, but got %v", envs)
t.Fatalf("calling getLaunchConfigs() with TLS disabled should have returned the proper environment variables, but got %v", envs)
}
}

Expand Down Expand Up @@ -716,7 +736,7 @@ func TestCCFramework(t *testing.T) {
getHistory(t, chainID, ccname, ccSide)

//just use the previous authhandler for generating TLS key/pair
getArgsAndEnv(t, theChaincodeSupport.auth)
getLaunchConfigs(t, theChaincodeSupport.auth)

ccSide.Quit()
}
19 changes: 17 additions & 2 deletions core/chaincode/shim/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"unicode/utf8"
Expand Down Expand Up @@ -88,8 +89,22 @@ var streamGetter peerStreamGetter
func userChaincodeStreamGetter(name string) (PeerChaincodeStream, error) {
flag.StringVar(&peerAddress, "peer.address", "", "peer address")
if comm.TLSEnabled() {
flag.StringVar(&key, "key", "", "key in BASE64")
flag.StringVar(&cert, "cert", "", "certificate in BASE64")
keyPath := viper.GetString("tls.client.key.path")
certPath := viper.GetString("tls.client.cert.path")

data, err1 := ioutil.ReadFile(keyPath)
if err1 != nil {
chaincodeLogger.Errorf("Error trying to read file content %s: %s", keyPath, err1)
return nil, fmt.Errorf("Error trying to read file content %s: %s", keyPath, err1)
}
key = string(data)

data, err1 = ioutil.ReadFile(certPath)
if err1 != nil {
chaincodeLogger.Errorf("Error trying to read file content %s: %s", certPath, err1)
return nil, fmt.Errorf("Error trying to read file content %s: %s", certPath, err1)
}
cert = string(data)
}

flag.Parse()
Expand Down
2 changes: 1 addition & 1 deletion core/container/api/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type PrelaunchFunc func() error
//VM is an abstract virtual image for supporting arbitrary virual machines
type VM interface {
Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error
Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder BuildSpecFactory, preLaunchFunc PrelaunchFunc) error
Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, filesToUpload map[string][]byte, builder BuildSpecFactory, preLaunchFunc PrelaunchFunc) error
Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error
Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, noprune bool) error
GetVMName(ccID ccintf.CCID, format func(string) (string, error)) (string, error)
Expand Down
3 changes: 2 additions & 1 deletion core/container/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,14 @@ type StartImageReq struct {
Builder api.BuildSpecFactory
Args []string
Env []string
FilesToUpload map[string][]byte
PrelaunchFunc api.PrelaunchFunc
}

func (si StartImageReq) do(ctxt context.Context, v api.VM) VMCResp {
var resp VMCResp

if err := v.Start(ctxt, si.CCID, si.Args, si.Env, si.Builder, si.PrelaunchFunc); err != nil {
if err := v.Start(ctxt, si.CCID, si.Args, si.Env, si.FilesToUpload, si.Builder, si.PrelaunchFunc); err != nil {
resp = VMCResp{Err: err}
} else {
resp = VMCResp{}
Expand Down
38 changes: 37 additions & 1 deletion core/container/dockercontroller/dockercontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package dockercontroller

import (
"archive/tar"
"bytes"
"compress/gzip"
"encoding/hex"
"fmt"
"io"
Expand Down Expand Up @@ -59,6 +61,9 @@ type DockerVM struct {
type dockerClient interface {
// CreateContainer creates a docker container, returns an error in case of failure
CreateContainer(opts docker.CreateContainerOptions) (*docker.Container, error)
// UploadToContainer uploads a tar archive to be extracted to a path in the
// filesystem of the container.
UploadToContainer(id string, opts docker.UploadToContainerOptions) error
// StartContainer starts a docker container, returns an error in case of failure
StartContainer(id string, cfg *docker.HostConfig) error
// AttachToContainer attaches to a docker container, returns an error in case of
Expand Down Expand Up @@ -211,7 +216,7 @@ func (vm *DockerVM) Deploy(ctxt context.Context, ccid ccintf.CCID,

//Start starts a container using a previously created docker image
func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID,
args []string, env []string, builder container.BuildSpecFactory, prelaunchFunc container.PrelaunchFunc) error {
args []string, env []string, filesToUpload map[string][]byte, builder container.BuildSpecFactory, prelaunchFunc container.PrelaunchFunc) error {
imageID, err := vm.GetVMName(ccid, formatImageName)
if err != nil {
return err
Expand Down Expand Up @@ -338,6 +343,37 @@ func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID,
}()
}

// upload specified files to the container before starting it
// this can be used for configurations such as TLS key and certs
if len(filesToUpload) != 0 {
// the docker upload API takes a tar file, so we need to first
// consolidate the file entries to a tar
payload := bytes.NewBuffer(nil)
gw := gzip.NewWriter(payload)
tw := tar.NewWriter(gw)

for path, fileToUpload := range filesToUpload {
cutil.WriteBytesToPackage(path, fileToUpload, tw)
}

// Write the tar file out
if err = tw.Close(); err != nil {
return fmt.Errorf("Error writing files to upload to Docker instance into a temporary tar blob: %s", err)
}

gw.Close()

err = client.UploadToContainer(containerID, docker.UploadToContainerOptions{
InputStream: bytes.NewReader(payload.Bytes()),
Path: "/",
NoOverwriteDirNonDir: false,
})

if err != nil {
return fmt.Errorf("Error uploading files to the container instance %s: %s", containerID, err)
}
}

if prelaunchFunc != nil {
if err = prelaunchFunc(); err != nil {
return err
Expand Down
Loading

0 comments on commit 22118e4

Please sign in to comment.