]>
Commit | Line | Data |
---|---|---|
a8f268af DM |
1 | //! Proxmox Backup Server Configuration library |
2 | //! | |
3 | //! This library contains helper to read, parse and write the | |
4 | //! configuration files. | |
5 | ||
f7d4e4b5 | 6 | use anyhow::{bail, format_err, Error}; |
f8fd5095 DM |
7 | use std::path::PathBuf; |
8 | use nix::sys::stat::Mode; | |
9 | use openssl::rsa::{Rsa}; | |
10 | use openssl::x509::{X509Builder}; | |
11 | use openssl::pkey::PKey; | |
a8f268af | 12 | |
f8fd5095 | 13 | use proxmox::tools::fs::{CreateOptions, replace_file}; |
9ea4bce4 | 14 | use proxmox::try_block; |
e18a6c9e | 15 | |
af06decd | 16 | use pbs_buildcfg::{self, configdir}; |
a8f268af | 17 | |
5c6cdf98 | 18 | pub mod acl; |
cb67ecad | 19 | pub mod acme; |
423e6561 | 20 | pub mod cached_user_info; |
90d515c9 | 21 | pub mod datastore; |
f34d4401 | 22 | pub mod network; |
79b902d5 | 23 | pub mod node; |
90d515c9 | 24 | pub mod remote; |
6f652b1b | 25 | pub mod sync; |
dc1fdd62 | 26 | pub mod tfa; |
f8adf8f8 | 27 | pub mod token_shadow; |
90d515c9 | 28 | pub mod user; |
9b2bad7a | 29 | pub mod verify; |
a0765714 | 30 | pub mod drive; |
1142350e | 31 | pub mod media_pool; |
d5a48b5c | 32 | pub mod tape_encryption_keys; |
be327dbc | 33 | pub mod tape_job; |
bbff6c49 | 34 | pub mod domains; |
5c20e2da | 35 | |
a8f268af DM |
36 | /// Check configuration directory permissions |
37 | /// | |
38 | /// For security reasons, we want to make sure they are set correctly: | |
39 | /// * owned by 'backup' user/group | |
40 | /// * nobody else can read (mode 0700) | |
8fdef1a8 | 41 | pub fn check_configdir_permissions() -> Result<(), Error> { |
af06decd | 42 | let cfgdir = pbs_buildcfg::CONFIGDIR; |
f74a03da DM |
43 | |
44 | let backup_user = crate::backup::backup_user()?; | |
45 | let backup_uid = backup_user.uid.as_raw(); | |
46 | let backup_gid = backup_user.gid.as_raw(); | |
a8f268af DM |
47 | |
48 | try_block!({ | |
49 | let stat = nix::sys::stat::stat(cfgdir)?; | |
50 | ||
51 | if stat.st_uid != backup_uid { | |
5c20e2da | 52 | bail!("wrong user ({} != {})", stat.st_uid, backup_uid); |
a8f268af DM |
53 | } |
54 | if stat.st_gid != backup_gid { | |
5c20e2da | 55 | bail!("wrong group ({} != {})", stat.st_gid, backup_gid); |
a8f268af DM |
56 | } |
57 | ||
58 | let perm = stat.st_mode & 0o777; | |
59 | if perm != 0o700 { | |
5c20e2da | 60 | bail!("wrong permission ({:o} != {:o})", perm, 0o700); |
a8f268af DM |
61 | } |
62 | Ok(()) | |
5c20e2da WB |
63 | }) |
64 | .map_err(|err| { | |
65 | format_err!( | |
66 | "configuration directory '{}' permission problem - {}", | |
67 | cfgdir, | |
68 | err | |
69 | ) | |
70 | }) | |
a8f268af DM |
71 | } |
72 | ||
73 | pub fn create_configdir() -> Result<(), Error> { | |
af06decd | 74 | let cfgdir = pbs_buildcfg::CONFIGDIR; |
a8f268af DM |
75 | |
76 | match nix::unistd::mkdir(cfgdir, Mode::from_bits_truncate(0o700)) { | |
5c20e2da | 77 | Ok(()) => {} |
a8f268af | 78 | Err(nix::Error::Sys(nix::errno::Errno::EEXIST)) => { |
8fdef1a8 | 79 | check_configdir_permissions()?; |
a8f268af | 80 | return Ok(()); |
5c20e2da WB |
81 | } |
82 | Err(err) => bail!( | |
83 | "unable to create configuration directory '{}' - {}", | |
84 | cfgdir, | |
85 | err | |
86 | ), | |
a8f268af DM |
87 | } |
88 | ||
f74a03da | 89 | let backup_user = crate::backup::backup_user()?; |
a8f268af | 90 | |
f74a03da DM |
91 | nix::unistd::chown(cfgdir, Some(backup_user.uid), Some(backup_user.gid)) |
92 | .map_err(|err| { | |
93 | format_err!( | |
94 | "unable to set configuration directory '{}' permissions - {}", | |
95 | cfgdir, | |
96 | err | |
97 | ) | |
98 | }) | |
a8f268af | 99 | } |
f8fd5095 DM |
100 | |
101 | /// Update self signed node certificate. | |
102 | pub fn update_self_signed_cert(force: bool) -> Result<(), Error> { | |
103 | ||
f8fd5095 DM |
104 | let key_path = PathBuf::from(configdir!("/proxy.key")); |
105 | let cert_path = PathBuf::from(configdir!("/proxy.pem")); | |
106 | ||
107 | if key_path.exists() && cert_path.exists() && !force { return Ok(()); } | |
108 | ||
109 | let rsa = Rsa::generate(4096).unwrap(); | |
110 | ||
111 | let priv_pem = rsa.private_key_to_pem()?; | |
112 | ||
f8fd5095 DM |
113 | let mut x509 = X509Builder::new()?; |
114 | ||
115 | x509.set_version(2)?; | |
116 | ||
117 | let today = openssl::asn1::Asn1Time::days_from_now(0)?; | |
118 | x509.set_not_before(&today)?; | |
119 | let expire = openssl::asn1::Asn1Time::days_from_now(365*1000)?; | |
120 | x509.set_not_after(&expire)?; | |
121 | ||
122 | let nodename = proxmox::tools::nodename(); | |
123 | let mut fqdn = nodename.to_owned(); | |
124 | ||
125 | let resolv_conf = crate::api2::node::dns::read_etc_resolv_conf()?; | |
126 | if let Some(search) = resolv_conf["search"].as_str() { | |
127 | fqdn.push('.'); | |
128 | fqdn.push_str(search); | |
129 | } | |
130 | ||
131 | // we try to generate an unique 'subject' to avoid browser problems | |
132 | //(reused serial numbers, ..) | |
133 | let uuid = proxmox::tools::uuid::Uuid::generate(); | |
134 | ||
135 | let mut subject_name = openssl::x509::X509NameBuilder::new()?; | |
136 | subject_name.append_entry_by_text("O", "Proxmox Backup Server")?; | |
137 | subject_name.append_entry_by_text("OU", &format!("{:X}", uuid))?; | |
138 | subject_name.append_entry_by_text("CN", &fqdn)?; | |
139 | let subject_name = subject_name.build(); | |
140 | ||
141 | x509.set_subject_name(&subject_name)?; | |
142 | x509.set_issuer_name(&subject_name)?; | |
143 | ||
144 | let bc = openssl::x509::extension::BasicConstraints::new(); // CA = false | |
145 | let bc = bc.build()?; | |
146 | x509.append_extension(bc)?; | |
147 | ||
148 | let usage = openssl::x509::extension::ExtendedKeyUsage::new() | |
149 | .server_auth() | |
150 | .build()?; | |
151 | x509.append_extension(usage)?; | |
152 | ||
153 | let context = x509.x509v3_context(None, None); | |
154 | ||
155 | let mut alt_names = openssl::x509::extension::SubjectAlternativeName::new(); | |
156 | ||
157 | alt_names.ip("127.0.0.1"); | |
158 | alt_names.ip("::1"); | |
159 | ||
160 | alt_names.dns("localhost"); | |
161 | ||
162 | if nodename != "localhost" { alt_names.dns(nodename); } | |
163 | if nodename != fqdn { alt_names.dns(&fqdn); } | |
164 | ||
165 | let alt_names = alt_names.build(&context)?; | |
166 | ||
167 | x509.append_extension(alt_names)?; | |
168 | ||
169 | let pub_pem = rsa.public_key_to_pem()?; | |
170 | let pubkey = PKey::public_key_from_pem(&pub_pem)?; | |
171 | ||
172 | x509.set_pubkey(&pubkey)?; | |
173 | ||
174 | let context = x509.x509v3_context(None, None); | |
175 | let ext = openssl::x509::extension::SubjectKeyIdentifier::new().build(&context)?; | |
176 | x509.append_extension(ext)?; | |
177 | ||
178 | let context = x509.x509v3_context(None, None); | |
179 | let ext = openssl::x509::extension::AuthorityKeyIdentifier::new() | |
180 | .keyid(true) | |
181 | .build(&context)?; | |
182 | x509.append_extension(ext)?; | |
183 | ||
184 | let privkey = PKey::from_rsa(rsa)?; | |
185 | ||
186 | x509.sign(&privkey, openssl::hash::MessageDigest::sha256())?; | |
187 | ||
188 | let x509 = x509.build(); | |
189 | let cert_pem = x509.to_pem()?; | |
190 | ||
fca1cef2 | 191 | set_proxy_certificate(&cert_pem, &priv_pem)?; |
f8fd5095 DM |
192 | |
193 | Ok(()) | |
194 | } | |
f912ba6a | 195 | |
fca1cef2 | 196 | pub(crate) fn set_proxy_certificate(cert_pem: &[u8], key_pem: &[u8]) -> Result<(), Error> { |
f912ba6a WB |
197 | let backup_user = crate::backup::backup_user()?; |
198 | let options = CreateOptions::new() | |
199 | .perm(Mode::from_bits_truncate(0o0640)) | |
200 | .owner(nix::unistd::ROOT) | |
201 | .group(backup_user.gid); | |
202 | let key_path = PathBuf::from(configdir!("/proxy.key")); | |
203 | let cert_path = PathBuf::from(configdir!("/proxy.pem")); | |
204 | ||
205 | create_configdir()?; | |
206 | replace_file(&key_path, &key_pem, options.clone()) | |
207 | .map_err(|err| format_err!("error writing certificate private key - {}", err))?; | |
208 | replace_file(&cert_path, &cert_pem, options) | |
209 | .map_err(|err| format_err!("error writing certificate file - {}", err))?; | |
4088d5bc | 210 | |
f912ba6a WB |
211 | Ok(()) |
212 | } |