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 `string_data` field
105    /// (i.e. plain text)
106    fn docker_config_json_text(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 `data` field
114    fn docker_config_json_binary(name: impl ToString, config: ByteString) -> Self {
115        let data = [(Self::DOCKER_CONFIG_JSON_KEY, config)];
116        Self::new(name)
117            .r#type(Self::SECRET_TYPE_DOCKER_CONFIG_JSON)
118            .data(data)
119    }
120
121    /// Creates new basic authentication secret object
122    fn basic_auth(name: impl ToString, username: impl ToString, password: impl ToString) -> Self {
123        let data = [
124            (Self::BASIC_AUTH_USERNAME_KEY, username.to_string()),
125            (Self::BASIC_AUTH_PASSWORD_KEY, password.to_string()),
126        ];
127        Self::new(name)
128            .r#type(Self::SECRET_TYPE_BASIC_AUTH)
129            .string_data(data)
130    }
131
132    /// Creates new ssh auth secret object
133    fn ssh_auth(name: impl ToString, private_key: impl ToString) -> Self {
134        let data = [(Self::SSH_AUTH_PRIVATE_KEY, private_key)];
135        Self::new(name)
136            .r#type(Self::SECRET_TYPE_SSH_AUTH)
137            .string_data(data)
138    }
139
140    /// Creates new image pull secret object
141    fn image_pull_secret(
142        name: impl ToString,
143        registry: impl ToString,
144        username: impl ToString,
145        password: impl ToString,
146    ) -> Self {
147        let registry = registry.to_string();
148        let username = username.to_string();
149        let password = password.to_string();
150        let auth = base64::prelude::BASE64_STANDARD.encode(format!("{username}:{password}"));
151        let config = format!(
152            r#"{{"auths":{{"{registry}":{{"username":"{username}","password":"{password}","auth":"{auth}"}}}}}}"#
153        );
154        Self::docker_config_json_text(name, config)
155    }
156}
157
158pub trait SecretExt2: SecretExt {
159    /// Creates new image pull secret object when you already have the .docker/config.json
160    /// content extracted from some other source (i.e. secret)
161    #[deprecated(
162        since = "0.0.54",
163        note = "use SecretExt::docker_config_json_text instead"
164    )]
165    fn image_pull_secret(name: impl ToString, config: impl ToString) -> Self {
166        Self::docker_config_json_text(name, config)
167    }
168}
169
170impl SecretExt for corev1::Secret {
171    fn new(name: impl ToString) -> Self {
172        let metadata = metadata(name);
173        Self {
174            metadata,
175            // immutable: todo!(),
176            // data: todo!(),
177            // string_data: todo!(),
178            // type_: todo!(),
179            ..default()
180        }
181    }
182
183    fn immutable(self, yes: bool) -> Self {
184        let immutable = Some(yes);
185        Self { immutable, ..self }
186    }
187
188    fn r#type(self, r#type: impl ToString) -> Self {
189        let type_ = Some(r#type.to_string());
190        Self { type_, ..self }
191    }
192
193    fn data(mut self, data: impl IntoIterator<Item = (impl ToString, ByteString)>) -> Self {
194        let iter = data
195            .into_iter()
196            .map(|(key, value)| (key.to_string(), value));
197        self.data.get_or_insert_default().extend(iter);
198        self
199    }
200
201    fn string_data(
202        mut self,
203        data: impl IntoIterator<Item = (impl ToString, impl ToString)>,
204    ) -> Self {
205        let iter = data
206            .into_iter()
207            .map(|(key, value)| (key.to_string(), value.to_string()));
208        self.string_data.get_or_insert_default().extend(iter);
209        self
210    }
211}
212
213impl SecretExt2 for corev1::Secret {}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    use serde_json as json;
220
221    #[test]
222    fn image_pull_secret() {
223        let secret = <corev1::Secret as SecretExt>::image_pull_secret(
224            "name", "registry", "username", "password",
225        );
226        let string_data = secret.string_data.unwrap_or_default();
227        assert_eq!(string_data.len(), 1);
228        let config: json::Value =
229            json::from_str(&string_data[corev1::Secret::DOCKER_CONFIG_JSON_KEY]).unwrap();
230        assert!(config.is_object());
231    }
232
233    #[test]
234    fn ssh_auth() {
235        let secret = corev1::Secret::ssh_auth(
236            "name",
237            "KGpwKaqlGas+LaAqdwdfAAAEEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIClTFvhvwp1UH25b",
238        );
239        let string_data = secret.string_data.unwrap_or_default();
240        assert_eq!(string_data.len(), 1);
241        assert_eq!(string_data[corev1::Secret::SSH_AUTH_PRIVATE_KEY].len(), 70);
242    }
243}