]> git.proxmox.com Git - proxmox-backup.git/blame - src/bin/proxmox_tape/encryption_key.rs
add missing file pbs-api-types/src/remote.rs
[proxmox-backup.git] / src / bin / proxmox_tape / encryption_key.rs
CommitLineData
feb1645f 1use anyhow::{bail, Error};
d5a48b5c
DM
2use serde_json::Value;
3
4use proxmox::{
5 api::{
6 api,
7 cli::*,
8 RpcEnvironment,
9 ApiHandler,
10 },
feb1645f 11 sys::linux::tty,
d5a48b5c
DM
12};
13
b2065dc7 14use pbs_api_types::Fingerprint;
86fb3877 15use pbs_datastore::Kdf;
eb5e0ae6 16use pbs_datastore::paperkey::{PaperkeyFormat, generate_paper_key};
86fb3877 17
d5a48b5c 18use 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
34pub 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.
95fn 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
127fn show_key(
128 param: Value,
129 rpcenv: &mut dyn RpcEnvironment,
130) -> Result<(), Error> {
131
132 let output_format = get_output_format(&param);
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.
170fn 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)
206async 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)
247fn 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
283fn list_keys(
284 param: Value,
285 rpcenv: &mut dyn RpcEnvironment,
286) -> Result<(), Error> {
287
288 let output_format = get_output_format(&param);
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}