k8s_openapi_ext/ext/
secret.rs

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