diff --git a/api/backup/restore.go b/api/backup/restore.go index fb47f25c5..e2b8ebb12 100644 --- a/api/backup/restore.go +++ b/api/backup/restore.go @@ -26,7 +26,7 @@ func RestoreArchive(archive io.Reader, password string, filestorePath string, ga if password != "" { archive, err = decrypt(archive, password) if err != nil { - return errors.Wrap(err, "failed to decrypt the archive") + return errors.Wrap(err, "failed to decrypt the archive. Please ensure the password is correct and try again") } } diff --git a/api/crypto/aes.go b/api/crypto/aes.go index 6dd273310..02046b362 100644 --- a/api/crypto/aes.go +++ b/api/crypto/aes.go @@ -1,52 +1,216 @@ package crypto import ( + "bufio" + "bytes" "crypto/aes" "crypto/cipher" + "crypto/rand" + "errors" + "fmt" "io" + "golang.org/x/crypto/argon2" "golang.org/x/crypto/scrypt" ) -// NOTE: has to go with what is considered to be a simplistic in that it omits any -// authentication of the encrypted data. -// Person with better knowledge is welcomed to improve it. -// sourced from https://golang.org/src/crypto/cipher/example_test.go +const ( + // AES GCM settings + aesGcmHeader = "AES256-GCM" // The encrypted file header + aesGcmBlockSize = 1024 * 1024 // 1MB block for aes gcm -var emptySalt []byte = make([]byte, 0) + // Argon2 settings + // Recommded settings lower memory hardware according to current OWASP recommendations + // Considering some people run portainer on a NAS I think it's prudent not to assume we're on server grade hardware + // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id + argon2MemoryCost = 12 * 1024 + argon2TimeCost = 3 + argon2Threads = 1 + argon2KeyLength = 32 +) -// AesEncrypt reads from input, encrypts with AES-256 and writes to the output. -// passphrase is used to generate an encryption key. +// AesEncrypt reads from input, encrypts with AES-256 and writes to output. passphrase is used to generate an encryption key func AesEncrypt(input io.Reader, output io.Writer, passphrase []byte) error { - // making a 32 bytes key that would correspond to AES-256 - // don't necessarily need a salt, so just kept in empty - key, err := scrypt.Key(passphrase, emptySalt, 32768, 8, 1, 32) + err := aesEncryptGCM(input, output, passphrase) + if err != nil { + return fmt.Errorf("error encrypting file: %w", err) + } + + return nil +} + +// AesDecrypt reads from input, decrypts with AES-256 and returns the reader to read the decrypted content from +func AesDecrypt(input io.Reader, passphrase []byte) (io.Reader, error) { + // Read file header to determine how it was encrypted + inputReader := bufio.NewReader(input) + header, err := inputReader.Peek(len(aesGcmHeader)) + if err != nil { + return nil, fmt.Errorf("error reading encrypted backup file header: %w", err) + } + + if string(header) == aesGcmHeader { + reader, err := aesDecryptGCM(inputReader, passphrase) + if err != nil { + return nil, fmt.Errorf("error decrypting file: %w", err) + } + + return reader, nil + } + + // Use the previous decryption routine which has no header (to support older archives) + reader, err := aesDecryptOFB(inputReader, passphrase) if err != nil { + return nil, fmt.Errorf("error decrypting legacy file backup: %w", err) + } + + return reader, nil +} + +// aesEncryptGCM reads from input, encrypts with AES-256 and writes to output. passphrase is used to generate an encryption key. +func aesEncryptGCM(input io.Reader, output io.Writer, passphrase []byte) error { + // Derive key using argon2 with a random salt + salt := make([]byte, 16) // 16 bytes salt + if _, err := io.ReadFull(rand.Reader, salt); err != nil { return err } + key := argon2.IDKey(passphrase, salt, argon2TimeCost, argon2MemoryCost, argon2Threads, 32) block, err := aes.NewCipher(key) if err != nil { return err } - // If the key is unique for each ciphertext, then it's ok to use a zero - // IV. - var iv [aes.BlockSize]byte - stream := cipher.NewOFB(block, iv[:]) + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return err + } - writer := &cipher.StreamWriter{S: stream, W: output} - // Copy the input to the output, encrypting as we go. - if _, err := io.Copy(writer, input); err != nil { + // Generate nonce + nonce, err := NewRandomNonce(aesgcm.NonceSize()) + if err != nil { return err } + // write the header + if _, err := output.Write([]byte(aesGcmHeader)); err != nil { + return err + } + + // Write nonce and salt to the output file + if _, err := output.Write(salt); err != nil { + return err + } + if _, err := output.Write(nonce.Value()); err != nil { + return err + } + + // Buffer for reading plaintext blocks + buf := make([]byte, aesGcmBlockSize) // Adjust buffer size as needed + ciphertext := make([]byte, len(buf)+aesgcm.Overhead()) + + // Encrypt plaintext in blocks + for { + n, err := io.ReadFull(input, buf) + if n == 0 { + break // end of plaintext input + } + + if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) { + return err + } + + // Seal encrypts the plaintext using the nonce returning the updated slice. + ciphertext = aesgcm.Seal(ciphertext[:0], nonce.Value(), buf[:n], nil) + + _, err = output.Write(ciphertext) + if err != nil { + return err + } + + nonce.Increment() + } + return nil } -// AesDecrypt reads from input, decrypts with AES-256 and returns the reader to a read decrypted content from. +// aesDecryptGCM reads from input, decrypts with AES-256 and returns the reader to read the decrypted content from. +func aesDecryptGCM(input io.Reader, passphrase []byte) (io.Reader, error) { + // Reader & verify header + header := make([]byte, len(aesGcmHeader)) + if _, err := io.ReadFull(input, header); err != nil { + return nil, err + } + + if string(header) != aesGcmHeader { + return nil, fmt.Errorf("invalid header") + } + + // Read salt + salt := make([]byte, 16) // Salt size + if _, err := io.ReadFull(input, salt); err != nil { + return nil, err + } + + key := argon2.IDKey(passphrase, salt, argon2TimeCost, argon2MemoryCost, argon2Threads, 32) + + // Initialize AES cipher block + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + // Create GCM mode with the cipher block + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + // Read nonce from the input reader + nonce := NewNonce(aesgcm.NonceSize()) + if err := nonce.Read(input); err != nil { + return nil, err + } + + // Initialize a buffer to store decrypted data + buf := bytes.Buffer{} + plaintext := make([]byte, aesGcmBlockSize) + + // Decrypt the ciphertext in blocks + for { + // Read a block of ciphertext from the input reader + ciphertextBlock := make([]byte, aesGcmBlockSize+aesgcm.Overhead()) // Adjust block size as needed + n, err := io.ReadFull(input, ciphertextBlock) + if n == 0 { + break // end of ciphertext + } + + if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) { + return nil, err + } + + // Decrypt the block of ciphertext + plaintext, err = aesgcm.Open(plaintext[:0], nonce.Value(), ciphertextBlock[:n], nil) + if err != nil { + return nil, err + } + + _, err = buf.Write(plaintext) + if err != nil { + return nil, err + } + + nonce.Increment() + } + + return &buf, nil +} + +// aesDecryptOFB reads from input, decrypts with AES-256 and returns the reader to a read decrypted content from. // passphrase is used to generate an encryption key. -func AesDecrypt(input io.Reader, passphrase []byte) (io.Reader, error) { +// note: This function used to decrypt files that were encrypted without a header i.e. old archives +func aesDecryptOFB(input io.Reader, passphrase []byte) (io.Reader, error) { + var emptySalt []byte = make([]byte, 0) + // making a 32 bytes key that would correspond to AES-256 // don't necessarily need a salt, so just kept in empty key, err := scrypt.Key(passphrase, emptySalt, 32768, 8, 1, 32) @@ -59,11 +223,9 @@ func AesDecrypt(input io.Reader, passphrase []byte) (io.Reader, error) { return nil, err } - // If the key is unique for each ciphertext, then it's ok to use a zero - // IV. + // If the key is unique for each ciphertext, then it's ok to use a zero IV. var iv [aes.BlockSize]byte stream := cipher.NewOFB(block, iv[:]) - reader := &cipher.StreamReader{S: stream, R: input} return reader, nil diff --git a/api/crypto/aes_test.go b/api/crypto/aes_test.go index 09dea9c6d..e03a9917e 100644 --- a/api/crypto/aes_test.go +++ b/api/crypto/aes_test.go @@ -2,6 +2,7 @@ package crypto import ( "io" + "math/rand" "os" "path/filepath" "testing" @@ -9,7 +10,19 @@ import ( "github.com/stretchr/testify/assert" ) +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +func randBytes(n int) []byte { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return b +} + func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) { + const passphrase = "passphrase" + tmpdir := t.TempDir() var ( @@ -18,17 +31,99 @@ func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) { decryptedFilePath = filepath.Join(tmpdir, "decrypted") ) - content := []byte("content") + content := randBytes(1024*1024*100 + 523) + os.WriteFile(originFilePath, content, 0600) + + originFile, _ := os.Open(originFilePath) + defer originFile.Close() + + encryptedFileWriter, _ := os.Create(encryptedFilePath) + + err := AesEncrypt(originFile, encryptedFileWriter, []byte(passphrase)) + assert.Nil(t, err, "Failed to encrypt a file") + encryptedFileWriter.Close() + + encryptedContent, err := os.ReadFile(encryptedFilePath) + assert.Nil(t, err, "Couldn't read encrypted file") + assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted") + + encryptedFileReader, _ := os.Open(encryptedFilePath) + defer encryptedFileReader.Close() + + decryptedFileWriter, _ := os.Create(decryptedFilePath) + defer decryptedFileWriter.Close() + + decryptedReader, err := AesDecrypt(encryptedFileReader, []byte(passphrase)) + assert.Nil(t, err, "Failed to decrypt file") + + io.Copy(decryptedFileWriter, decryptedReader) + + decryptedContent, _ := os.ReadFile(decryptedFilePath) + assert.Equal(t, content, decryptedContent, "Original and decrypted content should match") +} + +func Test_encryptAndDecrypt_withStrongPassphrase(t *testing.T) { + const passphrase = "A strong passphrase with special characters: !@#$%^&*()_+" + tmpdir := t.TempDir() + + var ( + originFilePath = filepath.Join(tmpdir, "origin2") + encryptedFilePath = filepath.Join(tmpdir, "encrypted2") + decryptedFilePath = filepath.Join(tmpdir, "decrypted2") + ) + + content := randBytes(500) + os.WriteFile(originFilePath, content, 0600) + + originFile, _ := os.Open(originFilePath) + defer originFile.Close() + + encryptedFileWriter, _ := os.Create(encryptedFilePath) + + err := AesEncrypt(originFile, encryptedFileWriter, []byte(passphrase)) + assert.Nil(t, err, "Failed to encrypt a file") + encryptedFileWriter.Close() + + encryptedContent, err := os.ReadFile(encryptedFilePath) + assert.Nil(t, err, "Couldn't read encrypted file") + assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted") + + encryptedFileReader, _ := os.Open(encryptedFilePath) + defer encryptedFileReader.Close() + + decryptedFileWriter, _ := os.Create(decryptedFilePath) + defer decryptedFileWriter.Close() + + decryptedReader, err := AesDecrypt(encryptedFileReader, []byte(passphrase)) + assert.Nil(t, err, "Failed to decrypt file") + + io.Copy(decryptedFileWriter, decryptedReader) + + decryptedContent, _ := os.ReadFile(decryptedFilePath) + assert.Equal(t, content, decryptedContent, "Original and decrypted content should match") +} + +func Test_encryptAndDecrypt_withTheSamePasswordSmallFile(t *testing.T) { + tmpdir := t.TempDir() + + var ( + originFilePath = filepath.Join(tmpdir, "origin2") + encryptedFilePath = filepath.Join(tmpdir, "encrypted2") + decryptedFilePath = filepath.Join(tmpdir, "decrypted2") + ) + + content := randBytes(500) os.WriteFile(originFilePath, content, 0600) originFile, _ := os.Open(originFilePath) defer originFile.Close() encryptedFileWriter, _ := os.Create(encryptedFilePath) - defer encryptedFileWriter.Close() err := AesEncrypt(originFile, encryptedFileWriter, []byte("passphrase")) assert.Nil(t, err, "Failed to encrypt a file") + encryptedFileWriter.Close() + encryptedContent, err := os.ReadFile(encryptedFilePath) assert.Nil(t, err, "Couldn't read encrypted file") assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted") @@ -57,7 +152,7 @@ func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) { decryptedFilePath = filepath.Join(tmpdir, "decrypted") ) - content := []byte("content") + content := randBytes(1024 * 50) os.WriteFile(originFilePath, content, 0600) originFile, _ := os.Open(originFilePath) @@ -96,7 +191,7 @@ func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T) decryptedFilePath = filepath.Join(tmpdir, "decrypted") ) - content := []byte("content") + content := randBytes(1034) os.WriteFile(originFilePath, content, 0600) originFile, _ := os.Open(originFilePath) @@ -117,11 +212,6 @@ func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T) decryptedFileWriter, _ := os.Create(decryptedFilePath) defer decryptedFileWriter.Close() - decryptedReader, err := AesDecrypt(encryptedFileReader, []byte("garbage")) - assert.Nil(t, err, "Should allow to decrypt with wrong passphrase") - - io.Copy(decryptedFileWriter, decryptedReader) - - decryptedContent, _ := os.ReadFile(decryptedFilePath) - assert.NotEqual(t, content, decryptedContent, "Original and decrypted content should NOT match") + _, err = AesDecrypt(encryptedFileReader, []byte("garbage")) + assert.NotNil(t, err, "Should not allow decrypt with wrong passphrase") } diff --git a/api/crypto/nonce.go b/api/crypto/nonce.go new file mode 100644 index 000000000..571a9ba71 --- /dev/null +++ b/api/crypto/nonce.go @@ -0,0 +1,61 @@ +package crypto + +import ( + "crypto/rand" + "errors" + "io" +) + +type Nonce struct { + val []byte +} + +func NewNonce(size int) *Nonce { + return &Nonce{val: make([]byte, size)} +} + +// NewRandomNonce generates a new initial nonce with the lower byte set to a random value +// This ensures there are plenty of nonce values availble before rolling over +// Based on ideas from the Secure Programming Cookbook for C and C++ by John Viega, Matt Messier +// https://www.oreilly.com/library/view/secure-programming-cookbook/0596003943/ch04s09.html +func NewRandomNonce(size int) (*Nonce, error) { + randomBytes := 1 + if size <= randomBytes { + return nil, errors.New("nonce size must be greater than the number of random bytes") + } + + randomPart := make([]byte, randomBytes) + if _, err := rand.Read(randomPart); err != nil { + return nil, err + } + + zeroPart := make([]byte, size-randomBytes) + nonceVal := append(randomPart, zeroPart...) + return &Nonce{val: nonceVal}, nil +} + +func (n *Nonce) Read(stream io.Reader) error { + _, err := io.ReadFull(stream, n.val) + return err +} + +func (n *Nonce) Value() []byte { + return n.val +} + +func (n *Nonce) Increment() error { + // Start incrementing from the least significant byte + for i := len(n.val) - 1; i >= 0; i-- { + // Increment the current byte + n.val[i]++ + + // Check for overflow + if n.val[i] != 0 { + // No overflow, nonce is successfully incremented + return nil + } + } + + // If we reach here, it means the nonce has overflowed + return errors.New("nonce overflow") +} diff --git a/go.mod b/go.mod index 438ed8627..dc6f56e14 100644 --- a/go.mod +++ b/go.mod @@ -76,15 +76,10 @@ require ( github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 // indirect github.com/aws/smithy-go v1.13.4 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/checkpoint-restore/go-criu/v5 v5.3.0 // indirect - github.com/cilium/ebpf v0.7.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/containerd/console v1.0.3 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/containers/ocicrypt v1.1.9 // indirect github.com/containers/storage v1.51.0 // indirect - github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.5.0 // indirect @@ -106,7 +101,6 @@ require ( github.com/go-openapi/swag v0.22.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/godbus/dbus/v5 v5.0.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -137,16 +131,12 @@ require ( github.com/moby/sys/mountinfo v0.7.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mrunalp/fileutils v0.5.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/opencontainers/runc v1.1.12 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect - github.com/opencontainers/selinux v1.11.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect github.com/segmentio/asm v1.1.3 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -155,10 +145,7 @@ require ( github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect github.com/ulikunitz/xz v0.5.11 // indirect - github.com/urfave/cli v1.22.12 // indirect github.com/vbatts/tar-split v0.11.5 // indirect - github.com/vishvananda/netlink v1.1.0 // indirect - github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect diff --git a/go.sum b/go.sum index c40e72ce7..9e9793942 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,6 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6 github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -62,16 +61,10 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containers/image/v5 v5.29.0 h1:9+nhS/ZM7c4Kuzu5tJ0NMpxrgoryOJ2HAYTgG8Ny7j4= github.com/containers/image/v5 v5.29.0/go.mod h1:kQ7qcDsps424ZAz24thD+x7+dJw1vgur3A9tTDsj97E= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= @@ -82,10 +75,7 @@ github.com/containers/storage v1.51.0 h1:AowbcpiWXzAjHosKz7MKvPEqpyX+ryZA/ZurytR github.com/containers/storage v1.51.0/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyObeKJzVtkUlfc= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -124,7 +114,6 @@ github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= @@ -168,8 +157,6 @@ github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6A github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -302,8 +289,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm/Q= -github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= @@ -314,14 +299,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= -github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= -github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -348,12 +329,7 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ= @@ -388,18 +364,12 @@ github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9 github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= -github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/viney-shih/go-lock v1.1.1 h1:SwzDPPAiHpcwGCr5k8xD15d2gQSo8d4roRYd7TDV2eI= github.com/viney-shih/go-lock v1.1.1/go.mod h1:Yijm78Ljteb3kRiJrbLAxVntkUukGu5uzSxq/xV7OO8= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -471,7 +441,6 @@ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -480,7 +449,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -552,7 +520,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=