]> git.proxmox.com Git - proxmox-backup.git/blob - src/bin/proxmox_tape/encryption_key.rs
907d2d636d76180d27c3573e60aa8ec43065ba3a
[proxmox-backup.git] / src / bin / proxmox_tape / encryption_key.rs
1 use anyhow::{bail, Error};
2 use serde_json::Value;
3
4 use proxmox::{
5 api::{
6 api,
7 cli::*,
8 RpcEnvironment,
9 ApiHandler,
10 },
11 sys::linux::tty,
12 };
13
14 use proxmox_backup::{
15 tools::{
16 self,
17 paperkey::{
18 PaperkeyFormat,
19 generate_paper_key,
20 },
21 },
22 config,
23 api2::{
24 self,
25 types::{
26 DRIVE_NAME_SCHEMA,
27 TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
28 PASSWORD_HINT_SCHEMA,
29 Kdf,
30 },
31 },
32 backup::Fingerprint,
33 config::tape_encryption_keys::{
34 load_key_configs,
35 complete_key_fingerprint,
36 },
37 };
38
39 pub fn encryption_key_commands() -> CommandLineInterface {
40
41 let cmd_def = CliCommandMap::new()
42 .insert("list", CliCommand::new(&API_METHOD_LIST_KEYS))
43 .insert(
44 "create",
45 CliCommand::new(&API_METHOD_CREATE_KEY)
46 )
47 .insert(
48 "change-passphrase",
49 CliCommand::new(&API_METHOD_CHANGE_PASSPHRASE)
50 .arg_param(&["fingerprint"])
51 .completion_cb("fingerprint", complete_key_fingerprint)
52 )
53 .insert(
54 "show",
55 CliCommand::new(&API_METHOD_SHOW_KEY)
56 .arg_param(&["fingerprint"])
57 .completion_cb("fingerprint", complete_key_fingerprint)
58 )
59 .insert(
60 "paperkey",
61 CliCommand::new(&API_METHOD_PAPER_KEY)
62 .arg_param(&["fingerprint"])
63 .completion_cb("fingerprint", complete_key_fingerprint)
64 )
65 .insert(
66 "restore",
67 CliCommand::new(&API_METHOD_RESTORE_KEY)
68 )
69 .insert(
70 "remove",
71 CliCommand::new(&api2::config::tape_encryption_keys::API_METHOD_DELETE_KEY)
72 .arg_param(&["fingerprint"])
73 .completion_cb("fingerprint", complete_key_fingerprint)
74 )
75 ;
76
77 cmd_def.into()
78 }
79
80 #[api(
81 input: {
82 properties: {
83 fingerprint: {
84 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
85 },
86 subject: {
87 description: "Include the specified subject as title text.",
88 optional: true,
89 },
90 "output-format": {
91 type: PaperkeyFormat,
92 optional: true,
93 },
94 },
95 },
96 )]
97 /// Generate a printable, human readable text file containing the encryption key.
98 ///
99 /// This also includes a scanable QR code for fast key restore.
100 fn paper_key(
101 fingerprint: Fingerprint,
102 subject: Option<String>,
103 output_format: Option<PaperkeyFormat>,
104 ) -> Result<(), Error> {
105
106 let (config_map, _digest) = load_key_configs()?;
107
108 let key_config = match config_map.get(&fingerprint) {
109 Some(key_config) => key_config,
110 None => bail!("tape encryption key '{}' does not exist.", fingerprint),
111 };
112
113 let data: String = serde_json::to_string_pretty(&key_config)?;
114
115 generate_paper_key(std::io::stdout(), &data, subject, output_format)
116 }
117
118 #[api(
119 input: {
120 properties: {
121 fingerprint: {
122 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
123 },
124 "output-format": {
125 schema: OUTPUT_FORMAT,
126 optional: true,
127 },
128 },
129 },
130 )]
131 /// Print the encryption key's metadata.
132 fn show_key(
133 param: Value,
134 rpcenv: &mut dyn RpcEnvironment,
135 ) -> Result<(), Error> {
136
137 let output_format = get_output_format(&param);
138
139 let info = &api2::config::tape_encryption_keys::API_METHOD_READ_KEY;
140 let mut data = match info.handler {
141 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
142 _ => unreachable!(),
143 };
144
145 let options = proxmox::api::cli::default_table_format_options()
146 .column(ColumnConfig::new("kdf"))
147 .column(ColumnConfig::new("created").renderer(tools::format::render_epoch))
148 .column(ColumnConfig::new("modified").renderer(tools::format::render_epoch))
149 .column(ColumnConfig::new("fingerprint"))
150 .column(ColumnConfig::new("hint"));
151
152 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
153
154 Ok(())
155 }
156
157 #[api(
158 input: {
159 properties: {
160 kdf: {
161 type: Kdf,
162 optional: true,
163 },
164 fingerprint: {
165 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
166 },
167 hint: {
168 schema: PASSWORD_HINT_SCHEMA,
169 optional: true,
170 },
171 },
172 },
173 )]
174 /// Change the encryption key's password.
175 fn change_passphrase(
176 mut param: Value,
177 rpcenv: &mut dyn RpcEnvironment,
178 ) -> Result<(), Error> {
179
180 if !tty::stdin_isatty() {
181 bail!("unable to change passphrase - no tty");
182 }
183
184 let password = tty::read_password("Current Tape Encryption Key Password: ")?;
185
186 let new_password = tty::read_and_verify_password("New Tape Encryption Key Password: ")?;
187
188 param["password"] = String::from_utf8(password)?.into();
189 param["new-password"] = String::from_utf8(new_password)?.into();
190
191 let info = &api2::config::tape_encryption_keys::API_METHOD_CHANGE_PASSPHRASE;
192 match info.handler {
193 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
194 _ => unreachable!(),
195 };
196
197 Ok(())
198 }
199
200 #[api(
201 input: {
202 properties: {
203 drive: {
204 schema: DRIVE_NAME_SCHEMA,
205 optional: true,
206 },
207 },
208 },
209 )]
210 /// Restore encryption key from tape (read password from stdin)
211 async fn restore_key(
212 mut param: Value,
213 rpcenv: &mut dyn RpcEnvironment,
214 ) -> Result<(), Error> {
215
216 let (config, _digest) = config::drive::config()?;
217 param["drive"] = crate::extract_drive_name(&mut param, &config)?.into();
218
219 if !tty::stdin_isatty() {
220 bail!("no password input mechanism available");
221 }
222
223 let password = tty::read_password("Tepe Encryption Key Password: ")?;
224 param["password"] = String::from_utf8(password)?.into();
225
226 let info = &api2::tape::drive::API_METHOD_RESTORE_KEY;
227 match info.handler {
228 ApiHandler::Async(handler) => (handler)(param, info, rpcenv).await?,
229 _ => unreachable!(),
230 };
231
232 Ok(())
233 }
234
235 #[api(
236 input: {
237 properties: {
238 kdf: {
239 type: Kdf,
240 optional: true,
241 },
242 hint: {
243 description: "Password restore hint.",
244 type: String,
245 min_length: 1,
246 max_length: 32,
247 },
248 },
249 },
250 )]
251 /// Create key (read password from stdin)
252 fn create_key(
253 mut param: Value,
254 rpcenv: &mut dyn RpcEnvironment,
255 ) -> Result<(), Error> {
256
257 if !tty::stdin_isatty() {
258 bail!("no password input mechanism available");
259 }
260
261 let password = tty::read_and_verify_password("Tape Encryption Key Password: ")?;
262
263 param["password"] = String::from_utf8(password)?.into();
264
265 let info = &api2::config::tape_encryption_keys::API_METHOD_CREATE_KEY;
266 let fingerprint = match info.handler {
267 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
268 _ => unreachable!(),
269 };
270
271 println!("{}", fingerprint);
272
273 Ok(())
274 }
275
276
277 #[api(
278 input: {
279 properties: {
280 "output-format": {
281 schema: OUTPUT_FORMAT,
282 optional: true,
283 },
284 },
285 },
286 )]
287 /// List keys
288 fn list_keys(
289 param: Value,
290 rpcenv: &mut dyn RpcEnvironment,
291 ) -> Result<(), Error> {
292
293 let output_format = get_output_format(&param);
294 let info = &api2::config::tape_encryption_keys::API_METHOD_LIST_KEYS;
295 let mut data = match info.handler {
296 ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
297 _ => unreachable!(),
298 };
299
300 let options = default_table_format_options()
301 .column(ColumnConfig::new("fingerprint"))
302 .column(ColumnConfig::new("hint"))
303 ;
304
305 format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
306
307 Ok(())
308 }