kubekrypt 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +13 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/README.md +91 -41
- data/lib/kubekrypt/cli.rb +70 -14
- data/lib/kubekrypt/version.rb +1 -1
- data/lib/kubekrypt.rb +3 -2
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fc12e50c4083f740004c80424c20d28abbdf9254be9e2c81752fa53c5b2ee9d
|
|
4
|
+
data.tar.gz: 1c72fef635ece42c16605893d8580234ff0efb63e147bd99ab6e9d08ee6b52a7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4cf83f02bda125ed63c86ef872b023aac37393b1523bda8c583e60eef46e48c0e9e85fecfc0b4625158746a5cc9e643544e2d1f8a176fa6adad28e4f122c9ec
|
|
7
|
+
data.tar.gz: 46ec255f4439a16e280f2cc666d16be278e463ea108cd8cfd3d8dedc7b1e73b0bc2917ba9f816d3d6ae64ab1dcf6e306fad6ca8686e7390a466293db424c0ffe
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.2.0] - 2026-03-24
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `--in-place` / `-i` flag for `encrypt` and `decrypt` to write output back to the source file
|
|
7
|
+
- Directory support — pass a directory path to process all `*.yaml`/`*.yml` files recursively
|
|
8
|
+
- `kind: Secret` validation — non-Secret manifests are rejected with a clear error
|
|
9
|
+
- YAML parse error handling — invalid YAML prints a clean error instead of a backtrace
|
|
10
|
+
- Dependabot configuration for automated dependency updates
|
|
11
|
+
|
|
12
|
+
## [2.1.1] - 2026-03-24
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Typos in error class names (`AlreadyEncryptedError`, `NotEncryptedError`)
|
|
16
|
+
- CLI now prints clean error messages instead of backtraces on failure
|
|
17
|
+
- Use `METADATA_KEY` constant consistently in CLI
|
|
18
|
+
|
|
3
19
|
## [2.1.0] - 2026-03-24
|
|
4
20
|
|
|
5
21
|
### Changed
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -1,86 +1,136 @@
|
|
|
1
1
|
[](https://rubygems.org/gems/kubekrypt)
|
|
2
|
+
[](https://github.com/knapo/kubekrypt/actions/workflows/ci.yml)
|
|
2
3
|
|
|
3
4
|
# KubeKrypt
|
|
4
5
|
|
|
5
|
-
A command-line tool for
|
|
6
|
+
A command-line tool for encrypting and decrypting Kubernetes Secret manifests using Google Cloud KMS.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
Secret manifests can be safely committed to version control in their encrypted form and decrypted on demand — either to inspect values or to pipe directly into `kubectl`.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
## Installation
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
```bash
|
|
13
|
+
gem install kubekrypt
|
|
14
|
+
```
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
- **Simple Interface**: Easy-to-use CLI commands for encryption and decryption
|
|
15
|
-
- **Metadata Tracking**: Embeds metadata in encrypted files for tracking and verification
|
|
16
|
-
- **Stdout Integration**: Outputs to standard out for easy piping and redirection
|
|
17
|
-
- **Base64 Processing**: Automatically handles base64 encoding/decoding under the hood, maintaining compatibility with Kubernetes Secret format
|
|
16
|
+
Or add it to your `Gemfile`:
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
```ruby
|
|
19
|
+
gem "kubekrypt"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Ruby >= 3.4
|
|
25
|
+
- A Google Cloud KMS key with `cloudkms.cryptoKeyVersions.useToEncrypt` / `useToDecrypt` permissions
|
|
26
|
+
|
|
27
|
+
## Authentication
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
You need one of:
|
|
29
|
+
KubeKrypt delegates authentication to the `google-cloud-kms` gem. Set one of the following environment variables:
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
| Variable | Description |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to a service account JSON key file |
|
|
34
|
+
| `GOOGLE_CLOUD_CREDENTIALS` | Path to JSON file, or JSON contents inline |
|
|
35
|
+
|
|
36
|
+
Application Default Credentials (e.g. `gcloud auth application-default login`) are also supported.
|
|
26
37
|
|
|
27
38
|
## Usage
|
|
28
39
|
|
|
29
|
-
###
|
|
40
|
+
### Encrypt a Secret
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
kubekrypt encrypt secret.yaml \
|
|
44
|
+
-k projects/my-project/locations/global/keyRings/my-ring/cryptoKeys/my-key \
|
|
45
|
+
> secret.enc.yaml
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Overwrite the file in place with `--in-place` / `-i`:
|
|
30
49
|
|
|
31
50
|
```bash
|
|
32
|
-
kubekrypt encrypt secret.yaml -k projects/
|
|
51
|
+
kubekrypt encrypt -i secret.yaml -k projects/my-project/...
|
|
33
52
|
```
|
|
34
53
|
|
|
35
|
-
|
|
54
|
+
Encrypt every Secret in a directory:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
kubekrypt encrypt secrets/ -k projects/my-project/...
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Each value under `data` is encrypted individually using KMS and prefixed with `enc:`. A `kubekrypt` metadata block is embedded in the output to record the KMS key used and the version:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
apiVersion: v1
|
|
64
|
+
kind: Secret
|
|
65
|
+
metadata:
|
|
66
|
+
name: my-secret
|
|
67
|
+
data:
|
|
68
|
+
token: enc:CiQAjMDkZ3...
|
|
69
|
+
kubekrypt:
|
|
70
|
+
kms_key: projects/my-project/locations/global/keyRings/my-ring/cryptoKeys/my-key
|
|
71
|
+
version: 2.1.1
|
|
72
|
+
modified_at: "2026-03-24T10:00:00Z"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Decrypt a Secret
|
|
36
76
|
|
|
37
77
|
```bash
|
|
38
78
|
kubekrypt decrypt secret.enc.yaml > secret.yaml
|
|
39
79
|
```
|
|
40
80
|
|
|
41
|
-
|
|
81
|
+
Or in place:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
kubekrypt decrypt -i secret.enc.yaml
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Decrypt every Secret in a directory:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
kubekrypt decrypt -i secrets/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The KMS key is read from the embedded `kubekrypt` metadata — no need to specify it again.
|
|
94
|
+
|
|
95
|
+
### Decrypt and apply directly with kubectl
|
|
96
|
+
|
|
97
|
+
Kubernetes Secret `data` values must be base64-encoded. Use `--base64` to have KubeKrypt re-encode the decrypted values before output:
|
|
42
98
|
|
|
43
99
|
```bash
|
|
44
100
|
kubekrypt decrypt --base64 secret.enc.yaml | kubectl apply -f -
|
|
45
101
|
```
|
|
46
102
|
|
|
47
|
-
###
|
|
103
|
+
### Check version
|
|
48
104
|
|
|
49
105
|
```bash
|
|
50
106
|
kubekrypt version
|
|
51
107
|
```
|
|
52
108
|
|
|
53
|
-
## How
|
|
109
|
+
## How it works
|
|
54
110
|
|
|
55
|
-
|
|
56
|
-
2. For encryption, it:
|
|
57
|
-
- Validates that it's a proper Kubernetes Secret
|
|
58
|
-
- Ensures it's not already encrypted
|
|
59
|
-
- Decodes base64 values to get raw data
|
|
60
|
-
- Uses Google Cloud KMS to encrypt sensitive data
|
|
61
|
-
- Re-encodes with base64 as needed
|
|
62
|
-
- Adds metadata about the encryption
|
|
63
|
-
- Outputs the encrypted YAML
|
|
111
|
+
**Encryption:**
|
|
64
112
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
113
|
+
1. Reads the YAML file and validates it is not already encrypted.
|
|
114
|
+
2. Calls Google Cloud KMS to encrypt each value in the `data` map.
|
|
115
|
+
3. Stores ciphertext as `enc:<base64>` in place of the original value.
|
|
116
|
+
4. Appends a `kubekrypt` metadata block with the KMS key name, gem version, and timestamp.
|
|
117
|
+
5. Prints the resulting YAML to stdout.
|
|
70
118
|
|
|
71
|
-
|
|
119
|
+
**Decryption:**
|
|
72
120
|
|
|
73
|
-
|
|
121
|
+
1. Reads the `kubekrypt` metadata block to determine which KMS key to use.
|
|
122
|
+
2. Calls Google Cloud KMS to decrypt each `enc:<base64>` value.
|
|
123
|
+
3. Strips the `kubekrypt` metadata block from the output.
|
|
124
|
+
4. Prints the resulting YAML to stdout (optionally with base64-encoded values via `--base64`).
|
|
74
125
|
|
|
75
|
-
##
|
|
126
|
+
## Security
|
|
76
127
|
|
|
77
|
-
|
|
78
|
-
- Access to a KMS key with appropriate permissions
|
|
128
|
+
KubeKrypt never stores or logs key material. All cryptographic operations are performed by Google Cloud KMS — plaintext values exist only transiently in memory during a command invocation.
|
|
79
129
|
|
|
80
130
|
## Contributing
|
|
81
131
|
|
|
82
|
-
|
|
132
|
+
Pull requests are welcome. Please make sure `bundle exec rake ci` passes before submitting.
|
|
83
133
|
|
|
84
134
|
## License
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
MIT — see [LICENSE](LICENSE) for details.
|
data/lib/kubekrypt/cli.rb
CHANGED
|
@@ -12,29 +12,85 @@ module KubeKrypt
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
method_option KMS_KEY, aliases: "-k", desc: "Google KMS encryption key id to use", required: true
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
content = YAML.safe_load(yaml_content)
|
|
15
|
+
method_option :in_place, aliases: "-i", desc: "Write output back to the source file", type: :boolean, default: false
|
|
16
|
+
desc "encrypt FILE [FILE ...]", "Encrypts Kubernetes Secret manifests using the specified KMS key"
|
|
17
|
+
def encrypt(*paths)
|
|
19
18
|
key_name = options.fetch(KMS_KEY)
|
|
19
|
+
in_place = options[:in_place]
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
result = KubeKrypt::Encryptor.call(content:, key_name:)
|
|
24
|
-
puts result
|
|
21
|
+
expand(paths).each { |file_path| process_encrypt(file_path, key_name:, in_place:) }
|
|
25
22
|
end
|
|
26
23
|
|
|
27
24
|
method_option :base64, desc: "Base64 encoded values", type: :boolean, required: false
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
content = YAML.safe_load(yaml_content)
|
|
25
|
+
method_option :in_place, aliases: "-i", desc: "Write output back to the source file", type: :boolean, default: false
|
|
26
|
+
desc "decrypt FILE [FILE ...]", "Decrypts Kubernetes Secret manifests using embedded kubekrypt metadata"
|
|
27
|
+
def decrypt(*paths)
|
|
32
28
|
base64 = options.fetch(:base64, false)
|
|
29
|
+
in_place = options[:in_place]
|
|
30
|
+
|
|
31
|
+
expand(paths).each { |file_path| process_decrypt(file_path, base64:, in_place:) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def expand(paths)
|
|
37
|
+
paths.flat_map do |path|
|
|
38
|
+
if File.directory?(path)
|
|
39
|
+
Dir.glob(File.join(path, "**", "*.{yaml,yml}")).sort
|
|
40
|
+
else
|
|
41
|
+
[path]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def load_secret(file_path)
|
|
47
|
+
content = YAML.safe_load_file(file_path)
|
|
48
|
+
raise InvalidSecretError, "#{file_path} is not a Kubernetes Secret" unless content["kind"] == "Secret"
|
|
49
|
+
content
|
|
50
|
+
rescue Psych::Exception => e
|
|
51
|
+
raise InvalidSecretError, "#{file_path} is not valid YAML: #{e.message}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def write_output(result, file_path:, in_place:)
|
|
55
|
+
if in_place
|
|
56
|
+
File.write(file_path, result)
|
|
57
|
+
else
|
|
58
|
+
puts result
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def process_encrypt(file_path, key_name:, in_place:)
|
|
63
|
+
content = load_secret(file_path)
|
|
64
|
+
raise AlreadyEncryptedError, "#{file_path} is already encrypted" if content[METADATA_KEY]
|
|
65
|
+
|
|
66
|
+
result = KubeKrypt::Encryptor.call(content:, key_name:)
|
|
67
|
+
write_output(result, file_path:, in_place:)
|
|
68
|
+
rescue AlreadyEncryptedError, NotEncryptedError, InvalidSecretError => e
|
|
69
|
+
warn "Error: #{e.message}"
|
|
70
|
+
exit 1
|
|
71
|
+
rescue Errno::ENOENT
|
|
72
|
+
warn "Error: file not found: #{file_path}"
|
|
73
|
+
exit 1
|
|
74
|
+
rescue => e
|
|
75
|
+
warn "Error: #{e.message}"
|
|
76
|
+
exit 1
|
|
77
|
+
end
|
|
33
78
|
|
|
34
|
-
|
|
79
|
+
def process_decrypt(file_path, base64:, in_place:)
|
|
80
|
+
content = load_secret(file_path)
|
|
81
|
+
raise NotEncryptedError, "#{file_path} is not encrypted" unless content[METADATA_KEY]
|
|
35
82
|
|
|
36
83
|
result = KubeKrypt::Decryptor.call(content:, base64:)
|
|
37
|
-
|
|
84
|
+
write_output(result, file_path:, in_place:)
|
|
85
|
+
rescue AlreadyEncryptedError, NotEncryptedError, InvalidSecretError => e
|
|
86
|
+
warn "Error: #{e.message}"
|
|
87
|
+
exit 1
|
|
88
|
+
rescue Errno::ENOENT
|
|
89
|
+
warn "Error: file not found: #{file_path}"
|
|
90
|
+
exit 1
|
|
91
|
+
rescue => e
|
|
92
|
+
warn "Error: #{e.message}"
|
|
93
|
+
exit 1
|
|
38
94
|
end
|
|
39
95
|
end
|
|
40
96
|
end
|
data/lib/kubekrypt/version.rb
CHANGED
data/lib/kubekrypt.rb
CHANGED
|
@@ -5,8 +5,9 @@ require "thor"
|
|
|
5
5
|
require "yaml"
|
|
6
6
|
|
|
7
7
|
module KubeKrypt
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
AlreadyEncryptedError = Class.new(StandardError)
|
|
9
|
+
NotEncryptedError = Class.new(StandardError)
|
|
10
|
+
InvalidSecretError = Class.new(StandardError)
|
|
10
11
|
KMS_KEY = :kms_key
|
|
11
12
|
ENCRYPTION_METHOD = "aes-256-gcm".freeze
|
|
12
13
|
METADATA_KEY = "kubekrypt".freeze
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kubekrypt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Krzysztof Knapik
|
|
@@ -72,6 +72,7 @@ executables:
|
|
|
72
72
|
extensions: []
|
|
73
73
|
extra_rdoc_files: []
|
|
74
74
|
files:
|
|
75
|
+
- ".github/dependabot.yml"
|
|
75
76
|
- ".github/workflows/ci.yml"
|
|
76
77
|
- ".gitignore"
|
|
77
78
|
- ".rspec"
|