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