github.com/zntrio/harp/v2@v2.0.9/pkg/tasks/container/seal.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package container 19 20 import ( 21 "context" 22 "crypto/rand" 23 "encoding/base64" 24 "encoding/json" 25 "errors" 26 "fmt" 27 28 "github.com/awnumar/memguard" 29 30 "github.com/zntrio/harp/v2/pkg/container" 31 "github.com/zntrio/harp/v2/pkg/container/seal" 32 sealv1 "github.com/zntrio/harp/v2/pkg/container/seal/v1" 33 sealv2 "github.com/zntrio/harp/v2/pkg/container/seal/v2" 34 "github.com/zntrio/harp/v2/pkg/sdk/types" 35 "github.com/zntrio/harp/v2/pkg/tasks" 36 ) 37 38 // SealTask implements secret container sealing task. 39 type SealTask struct { 40 ContainerReader tasks.ReaderProvider 41 SealedContainerWriter tasks.WriterProvider 42 OutputWriter tasks.WriterProvider 43 PeerPublicKeys []string 44 DCKDMasterKey string 45 DCKDTarget string 46 JSONOutput bool 47 DisableContainerIdentity bool 48 SealVersion uint 49 PreSharedKey *memguard.LockedBuffer 50 } 51 52 // Run the task. 53 // 54 //nolint:funlen,gocyclo // to refactor 55 func (t *SealTask) Run(ctx context.Context) error { 56 // Check arguments 57 if types.IsNil(t.ContainerReader) { 58 return errors.New("unable to run task with a nil containerReader provider") 59 } 60 if types.IsNil(t.SealedContainerWriter) { 61 return errors.New("unable to run task with a nil sealedContainerWriter provider") 62 } 63 if types.IsNil(t.OutputWriter) { 64 return errors.New("unable to run task with a nil outputWriter provider") 65 } 66 if len(t.PeerPublicKeys) == 0 { 67 return errors.New("at least one public key must be provided for recovery") 68 } 69 70 // Create input reader 71 reader, err := t.ContainerReader(ctx) 72 if err != nil { 73 return fmt.Errorf("unable to open input reader: %w", err) 74 } 75 76 // Load input container 77 in, err := container.Load(reader) 78 if err != nil { 79 return fmt.Errorf("unable to read input container: %w", err) 80 } 81 82 var containerKey string 83 if !t.DisableContainerIdentity { 84 opts := []seal.GenerateOption{} 85 86 // Check container sealing master key usage 87 if t.DCKDMasterKey != "" { 88 // Process target 89 if t.DCKDTarget == "" { 90 return errors.New("target flag (string) is mandatory for key derivation") 91 } 92 93 // Decode master key 94 masterKeyRaw, errDecode := base64.RawURLEncoding.DecodeString(t.DCKDMasterKey) 95 if errDecode != nil { 96 return fmt.Errorf("unable to decode master key: %w", errDecode) 97 } 98 99 // Enable deterministic container key generation 100 opts = append(opts, seal.WithDeterministicKey(memguard.NewBufferFromBytes(masterKeyRaw), t.DCKDTarget)) 101 } 102 103 // Initialize seal strategy 104 var ss seal.Strategy 105 switch t.SealVersion { 106 case 1: 107 ss = sealv1.New() 108 case 2: 109 ss = sealv2.New() 110 default: 111 ss = sealv1.New() 112 } 113 114 // Generate container key 115 containerPublicKey, containerSecretKey, errGenerate := ss.GenerateKey(opts...) 116 if errGenerate != nil { 117 return fmt.Errorf("unable to generate container key: %w", errGenerate) 118 } 119 120 // Append to identities 121 t.PeerPublicKeys = append(t.PeerPublicKeys, containerPublicKey) 122 123 // Assign container key 124 containerKey = containerSecretKey 125 } 126 127 // Seal options 128 sopts := []container.Option{ 129 container.WithPeerPublicKeys(t.PeerPublicKeys), 130 } 131 132 // Process pre-shared key 133 if t.PreSharedKey != nil { 134 // Try to decode preshared key 135 psk, errDecode := base64.RawURLEncoding.DecodeString(t.PreSharedKey.String()) 136 if errDecode != nil { 137 return fmt.Errorf("unable to decode pre-shared key: %w", errDecode) 138 } 139 sopts = append(sopts, container.WithPreSharedKey(memguard.NewBufferFromBytes(psk))) 140 t.PreSharedKey.Destroy() 141 } 142 143 // Seal the container 144 sealedContainer, err := container.Seal(rand.Reader, in, sopts...) 145 if err != nil { 146 return fmt.Errorf("unable to seal container: %w", err) 147 } 148 149 // Open output file 150 writer, err := t.SealedContainerWriter(ctx) 151 if err != nil { 152 return fmt.Errorf("unable to create output bundle: %w", err) 153 } 154 155 // Dump to writer 156 if err = container.Dump(writer, sealedContainer); err != nil { 157 return fmt.Errorf("unable to write sealed container: %w", err) 158 } 159 160 if !t.DisableContainerIdentity { 161 // Get output writer 162 outputWriter, err := t.OutputWriter(ctx) 163 if err != nil { 164 return fmt.Errorf("unable to retrieve output writer: %w", err) 165 } 166 167 // Display as json 168 if t.JSONOutput { 169 if err := json.NewEncoder(outputWriter).Encode(map[string]interface{}{ 170 "container_key": containerKey, 171 }); err != nil { 172 return fmt.Errorf("unable to display as json: %w", err) 173 } 174 } else { 175 // Display container key 176 if _, err := fmt.Fprintf(outputWriter, "Container key : %s\n", containerKey); err != nil { 177 return fmt.Errorf("unable to display result: %w", err) 178 } 179 } 180 } 181 182 // No error 183 return nil 184 }