1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
use base64::Engine;
use super::*;
pub trait SecretExt: super::ResourceBuilder + Sized {
/// SecretTypeOpaque is the default; arbitrary user-defined data
const SECRET_TYPE_OPAQUE: &str = "Opaque";
/// SecretTypeServiceAccountToken contains a token that identifies a service account to the API
///
/// Required fields:
/// - Secret.Annotations["kubernetes.io/service-account.name"] - the name of the ServiceAccount the token identifies
/// - Secret.Annotations["kubernetes.io/service-account.uid"] - the UID of the ServiceAccount the token identifies
/// - Secret.Data["token"] - a token that identifies the service account to the API
const SECRET_TYPE_SERVICE_ACCOUNT_TOKEN: &str = "kubernetes.io/service-account-token";
/// ServiceAccountNameKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
const SERVICE_ACCOUNT_NAME_KEY: &str = "kubernetes.io/service-account.name";
/// ServiceAccountUIDKey is the key of the required annotation for SecretTypeServiceAccountToken secrets
const SERVICE_ACCOUNT_UID_KEY: &str = "kubernetes.io/service-account.uid";
/// ServiceAccountTokenKey is the key of the required data for SecretTypeServiceAccountToken secrets
const SERVICE_ACCOUNT_TOKEN_KEY: &str = "token";
/// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
const SERVICE_ACCOUNT_KUBECONFIG_KEY: &str = "kubernetes.kubeconfig";
/// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
const SERVICE_ACCOUNT_ROOTCA_KEY: &str = "ca.crt";
/// ServiceAccountNamespaceKey is the key of the optional namespace to use as the default for namespaced API calls
const SERVICE_ACCOUNT_NAMESPACE_KEY: &str = "namespace";
/// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
///
/// Required fields:
/// - Secret.Data[".dockercfg"] - a serialized ~/.dockercfg file
const SECRET_TYPE_DOCKERCFG: &str = "kubernetes.io/dockercfg";
/// DockerConfigKey is the key of the required data for SecretTypeDockercfg secrets
const DOCKER_CONFIG_KEY: &str = ".dockercfg";
/// SecretTypeDockerConfigJSON contains a dockercfg file that follows the same format rules as ~/.docker/config.json
///
/// Required fields:
/// - Secret.Data[".dockerconfigjson"] - a serialized ~/.docker/config.json file
const SECRET_TYPE_DOCKER_CONFIG_JSON: &str = "kubernetes.io/dockerconfigjson";
/// DockerConfigJSONKey is the key of the required data for SecretTypeDockerConfigJson secrets
const DOCKER_CONFIG_JSON_KEY: &str = ".dockerconfigjson";
/// SecretTypeBasicAuth contains data needed for basic authentication.
///
/// Required at least one of fields:
/// - Secret.Data["username"] - username used for authentication
/// - Secret.Data["password"] - password or token needed for authentication
const SECRET_TYPE_BASIC_AUTH: &str = "kubernetes.io/basic-auth";
/// BasicAuthUsernameKey is the key of the username for SecretTypeBasicAuth secrets
const BASIC_AUTH_USERNAME_KEY: &str = "username";
/// BasicAuthPasswordKey is the key of the password or token for SecretTypeBasicAuth secrets
const BASIC_AUTH_PASSWORD_KEY: &str = "password";
/// SecretTypeSSHAuth contains data needed for SSH authentication.
///
/// Required field:
/// - Secret.Data["ssh-privatekey"] - private SSH key needed for authentication
const SECRET_TYPE_SSH_AUTH: &str = "kubernetes.io/ssh-auth";
/// SSHAuthPrivateKey is the key of the required SSH private key for SecretTypeSSHAuth secrets
const SSH_AUTH_PRIVATE_KEY: &str = "ssh-privatekey";
/// SecretTypeTLS contains information about a TLS client or server secret. It
/// is primarily used with TLS termination of the Ingress resource, but may be
/// used in other types.
///
/// Required fields:
/// - Secret.Data["tls.key"] - TLS private key.
/// Secret.Data["tls.crt"] - TLS certificate.
const SECRET_TYPE_TLS: &str = "kubernetes.io/tls";
/// TLSCertKey is the key for tls certificates in a TLS secret.
const TLS_CERT_KEY: &str = "tls.crt";
/// TLSPrivateKeyKey is the key for the private key field in a TLS secret.
const TLS_PRIVATE_KEY_KEY: &str = "tls.key";
/// SecretTypeBootstrapToken is used during the automated bootstrap process (first
/// implemented by kubeadm). It stores tokens that are used to sign well known
/// ConfigMaps. They are used for authn.
const SECRET_TYPE_BOOTSTRAP_TOKEN: &str = "bootstrap.kubernetes.io/token";
/// Creates new empty `corev1::Secret` object
fn new(name: impl ToString) -> Self;
/// Set immutability field
fn immutable(self, yes: bool) -> Self;
fn r#type(self, r#type: impl ToString) -> Self;
fn data(self, data: impl IntoIterator<Item = (impl ToString, ByteString)>) -> Self;
fn string_data(self, data: impl IntoIterator<Item = (impl ToString, impl ToString)>) -> Self;
/// Create new "Opaque" secret object with no data
fn opaque(name: impl ToString) -> Self {
Self::new(name).r#type(Self::SECRET_TYPE_OPAQUE)
}
/// Create "kubernetes.io/dockerconfigjson" secret with `config` as `string_data` field
/// (i.e. plain text)
fn docker_config_json_text(name: impl ToString, config: impl ToString) -> Self {
let data = [(Self::DOCKER_CONFIG_JSON_KEY, config)];
Self::new(name)
.r#type(Self::SECRET_TYPE_DOCKER_CONFIG_JSON)
.string_data(data)
}
/// Create "kubernetes.io/dockerconfigjson" secret with `config` as `data` field
fn docker_config_json_binary(name: impl ToString, config: ByteString) -> Self {
let data = [(Self::DOCKER_CONFIG_JSON_KEY, config)];
Self::new(name)
.r#type(Self::SECRET_TYPE_DOCKER_CONFIG_JSON)
.data(data)
}
/// Creates new basic authentication secret object
fn basic_auth(name: impl ToString, username: impl ToString, password: impl ToString) -> Self {
let data = [
(Self::BASIC_AUTH_USERNAME_KEY, username.to_string()),
(Self::BASIC_AUTH_PASSWORD_KEY, password.to_string()),
];
Self::new(name)
.r#type(Self::SECRET_TYPE_BASIC_AUTH)
.string_data(data)
}
/// Creates new ssh auth secret object
fn ssh_auth(name: impl ToString, private_key: impl ToString) -> Self {
let data = [(Self::SSH_AUTH_PRIVATE_KEY, private_key)];
Self::new(name)
.r#type(Self::SECRET_TYPE_SSH_AUTH)
.string_data(data)
}
/// Creates new image pull secret object
fn image_pull_secret(
name: impl ToString,
registry: impl ToString,
username: impl ToString,
password: impl ToString,
) -> Self {
let registry = registry.to_string();
let username = username.to_string();
let password = password.to_string();
let auth = base64::prelude::BASE64_STANDARD.encode(format!("{username}:{password}"));
let config = format!(
r#"{{"auths":{{"{registry}":{{"username":"{username}","password":"{password}","auth":"{auth}"}}}}}}"#
);
Self::docker_config_json_text(name, config)
}
}
pub trait SecretExt2: SecretExt {
/// Creates new image pull secret object when you already have the .docker/config.json
/// content extracted from some other source (i.e. secret)
#[deprecated(
since = "0.0.54",
note = "use SecretExt::docker_config_json_text instead"
)]
fn image_pull_secret(name: impl ToString, config: impl ToString) -> Self {
Self::docker_config_json_text(name, config)
}
}
impl SecretExt for corev1::Secret {
fn new(name: impl ToString) -> Self {
let metadata = metadata(name);
Self {
metadata,
// immutable: todo!(),
// data: todo!(),
// string_data: todo!(),
// type_: todo!(),
..default()
}
}
fn immutable(self, yes: bool) -> Self {
let immutable = Some(yes);
Self { immutable, ..self }
}
fn r#type(self, r#type: impl ToString) -> Self {
let type_ = Some(r#type.to_string());
Self { type_, ..self }
}
fn data(mut self, data: impl IntoIterator<Item = (impl ToString, ByteString)>) -> Self {
let iter = data
.into_iter()
.map(|(key, value)| (key.to_string(), value));
self.data.get_or_insert_default().extend(iter);
self
}
fn string_data(
mut self,
data: impl IntoIterator<Item = (impl ToString, impl ToString)>,
) -> Self {
let iter = data
.into_iter()
.map(|(key, value)| (key.to_string(), value.to_string()));
self.string_data.get_or_insert_default().extend(iter);
self
}
}
impl SecretExt2 for corev1::Secret {}
#[cfg(test)]
mod tests {
use super::*;
use serde_json as json;
#[test]
fn image_pull_secret() {
let secret = <corev1::Secret as SecretExt>::image_pull_secret(
"name", "registry", "username", "password",
);
let string_data = secret.string_data.unwrap_or_default();
assert_eq!(string_data.len(), 1);
let config: json::Value =
json::from_str(&string_data[corev1::Secret::DOCKER_CONFIG_JSON_KEY]).unwrap();
assert!(config.is_object());
}
#[test]
fn ssh_auth() {
let secret = corev1::Secret::ssh_auth(
"name",
"KGpwKaqlGas+LaAqdwdfAAAEEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIClTFvhvwp1UH25b",
);
let string_data = secret.string_data.unwrap_or_default();
assert_eq!(string_data.len(), 1);
assert_eq!(string_data[corev1::Secret::SSH_AUTH_PRIVATE_KEY].len(), 70);
}
}