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