ferron/util/
env_config.rs1use std::env;
2use yaml_rust2::Yaml;
3
4pub fn apply_env_vars_to_config(yaml_config: &mut Yaml) {
7 let global_hash = match yaml_config["global"].as_mut_hash() {
8 Some(h) => h,
9 None => return,
10 };
11
12 if let Ok(port_val) = env::var("FERRON_PORT") {
14 if let Ok(port) = port_val.parse::<i64>() {
15 global_hash.insert(Yaml::String("port".into()), Yaml::Integer(port));
16 } else {
17 global_hash.insert(Yaml::String("port".into()), Yaml::String(port_val));
19 }
20 }
21
22 if let Ok(sport_val) = env::var("FERRON_SPORT") {
23 if let Ok(sport) = sport_val.parse::<i64>() {
24 global_hash.insert(Yaml::String("sport".into()), Yaml::Integer(sport));
25 } else {
26 global_hash.insert(Yaml::String("sport".into()), Yaml::String(sport_val));
28 }
29 }
30
31 let http2_initial_window = env::var("FERRON_HTTP2_INITIAL_WINDOW_SIZE")
33 .ok()
34 .and_then(|val| val.parse::<i64>().ok());
35 let http2_max_frame = env::var("FERRON_HTTP2_MAX_FRAME_SIZE")
36 .ok()
37 .and_then(|val| val.parse::<i64>().ok());
38 let http2_max_streams = env::var("FERRON_HTTP2_MAX_CONCURRENT_STREAMS")
39 .ok()
40 .and_then(|val| val.parse::<i64>().ok());
41 let http2_max_header = env::var("FERRON_HTTP2_MAX_HEADER_LIST_SIZE")
42 .ok()
43 .and_then(|val| val.parse::<i64>().ok());
44 let http2_enable_connect = env::var("FERRON_HTTP2_ENABLE_CONNECT_PROTOCOL")
45 .ok()
46 .map(|val| matches!(val.to_ascii_lowercase().as_str(), "1" | "true" | "yes"));
47
48 if http2_initial_window.is_some()
50 || http2_max_frame.is_some()
51 || http2_max_streams.is_some()
52 || http2_max_header.is_some()
53 || http2_enable_connect.is_some()
54 {
55 let mut http2_hash = yaml_rust2::yaml::Hash::new();
56
57 if let Some(size) = http2_initial_window {
59 http2_hash.insert(
60 Yaml::String("initialWindowSize".into()),
61 Yaml::Integer(size),
62 );
63 }
64
65 if let Some(size) = http2_max_frame {
66 http2_hash.insert(Yaml::String("maxFrameSize".into()), Yaml::Integer(size));
67 }
68
69 if let Some(streams) = http2_max_streams {
70 http2_hash.insert(
71 Yaml::String("maxConcurrentStreams".into()),
72 Yaml::Integer(streams),
73 );
74 }
75
76 if let Some(size) = http2_max_header {
77 http2_hash.insert(
78 Yaml::String("maxHeaderListSize".into()),
79 Yaml::Integer(size),
80 );
81 }
82
83 if let Some(enable) = http2_enable_connect {
84 http2_hash.insert(
85 Yaml::String("enableConnectProtocol".into()),
86 Yaml::Boolean(enable),
87 );
88 }
89
90 if !http2_hash.is_empty() {
92 global_hash.insert(Yaml::String("http2Settings".into()), Yaml::Hash(http2_hash));
93 }
94 }
95
96 if let Ok(path) = env::var("FERRON_LOG_FILE_PATH") {
98 global_hash.insert(Yaml::String("logFilePath".into()), Yaml::String(path));
99 }
100
101 if let Ok(path) = env::var("FERRON_ERROR_LOG_FILE_PATH") {
102 global_hash.insert(Yaml::String("errorLogFilePath".into()), Yaml::String(path));
103 }
104
105 if let Ok(cert) = env::var("FERRON_CERT") {
107 global_hash.insert(Yaml::String("cert".into()), Yaml::String(cert));
108 }
109
110 if let Ok(key) = env::var("FERRON_KEY") {
111 global_hash.insert(Yaml::String("key".into()), Yaml::String(key));
112 }
113
114 if let Ok(min_ver) = env::var("FERRON_TLS_MIN_VERSION") {
115 global_hash.insert(Yaml::String("tlsMinVersion".into()), Yaml::String(min_ver));
116 }
117
118 if let Ok(max_ver) = env::var("FERRON_TLS_MAX_VERSION") {
119 global_hash.insert(Yaml::String("tlsMaxVersion".into()), Yaml::String(max_ver));
120 }
121
122 if let Ok(v) = env::var("FERRON_SECURE") {
124 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
125 global_hash.insert(Yaml::String("secure".into()), Yaml::Boolean(enable));
126 }
127
128 if let Ok(v) = env::var("FERRON_ENABLE_HTTP2") {
129 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
130 global_hash.insert(Yaml::String("enableHTTP2".into()), Yaml::Boolean(enable));
131 }
132
133 if let Ok(v) = env::var("FERRON_ENABLE_HTTP3") {
134 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
135 global_hash.insert(Yaml::String("enableHTTP3".into()), Yaml::Boolean(enable));
136 }
137
138 if let Ok(v) = env::var("FERRON_DISABLE_NON_ENCRYPTED_SERVER") {
139 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
140 global_hash.insert(
141 Yaml::String("disableNonEncryptedServer".into()),
142 Yaml::Boolean(enable),
143 );
144 }
145
146 if let Ok(v) = env::var("FERRON_ENABLE_OCSP_STAPLING") {
147 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
148 global_hash.insert(
149 Yaml::String("enableOCSPStapling".into()),
150 Yaml::Boolean(enable),
151 );
152 }
153
154 if let Ok(v) = env::var("FERRON_ENABLE_DIRECTORY_LISTING") {
155 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
156 global_hash.insert(
157 Yaml::String("enableDirectoryListing".into()),
158 Yaml::Boolean(enable),
159 );
160 }
161
162 if let Ok(v) = env::var("FERRON_ENABLE_COMPRESSION") {
163 let enable = matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes");
164 global_hash.insert(
165 Yaml::String("enableCompression".into()),
166 Yaml::Boolean(enable),
167 );
168 }
169
170 if let Ok(list) = env::var("FERRON_LOAD_MODULES") {
172 let arr: Vec<Yaml> = list
173 .split(',')
174 .filter_map(|s| {
175 let t = s.trim();
176 if t.is_empty() {
177 None
178 } else {
179 Some(Yaml::String(t.to_string()))
180 }
181 })
182 .collect();
183 if !arr.is_empty() {
184 global_hash.insert(Yaml::String("loadModules".into()), Yaml::Array(arr));
185 }
186 }
187
188 if let Ok(list) = env::var("FERRON_BLOCKLIST") {
190 let arr: Vec<Yaml> = list
191 .split(',')
192 .filter_map(|s| {
193 let t = s.trim();
194 if t.is_empty() {
195 None
196 } else {
197 Some(Yaml::String(t.to_string()))
198 }
199 })
200 .collect();
201 if !arr.is_empty() {
202 global_hash.insert(Yaml::String("blocklist".into()), Yaml::Array(arr));
203 }
204 }
205
206 if let Ok(sni_hosts) = env::var("FERRON_SNI_HOSTS") {
208 let hosts: Vec<&str> = sni_hosts
209 .split(',')
210 .map(|s| s.trim())
211 .filter(|s| !s.is_empty())
212 .collect();
213
214 if !hosts.is_empty() {
215 let mut sni_hash = yaml_rust2::yaml::Hash::new();
216
217 for host in hosts {
218 let cert_env_var = format!(
219 "FERRON_SNI_{}_CERT",
220 host
221 .replace('.', "_")
222 .replace('*', "WILDCARD")
223 .to_uppercase()
224 );
225 let key_env_var = format!(
226 "FERRON_SNI_{}_KEY",
227 host
228 .replace('.', "_")
229 .replace('*', "WILDCARD")
230 .to_uppercase()
231 );
232
233 if let (Ok(cert), Ok(key)) = (env::var(&cert_env_var), env::var(&key_env_var)) {
234 let mut host_hash = yaml_rust2::yaml::Hash::new();
235 host_hash.insert(Yaml::String("cert".into()), Yaml::String(cert));
236 host_hash.insert(Yaml::String("key".into()), Yaml::String(key));
237 sni_hash.insert(Yaml::String(host.to_string()), Yaml::Hash(host_hash));
238 }
239 }
240
241 if !sni_hash.is_empty() {
242 global_hash.insert(Yaml::String("sni".into()), Yaml::Hash(sni_hash));
243 }
244 }
245 }
246
247 if let Ok(env_list) = env::var("FERRON_ENV_VARS") {
249 let vars: Vec<&str> = env_list
250 .split(',')
251 .map(|s| s.trim())
252 .filter(|s| !s.is_empty())
253 .collect();
254
255 if !vars.is_empty() {
256 let mut env_hash = yaml_rust2::yaml::Hash::new();
257
258 for var_name in vars {
259 let env_var = format!("FERRON_ENV_{}", var_name.to_uppercase());
260
261 if let Ok(value) = env::var(&env_var) {
262 env_hash.insert(Yaml::String(var_name.to_string()), Yaml::String(value));
263 }
264 }
265
266 if !env_hash.is_empty() {
267 global_hash.insert(
268 Yaml::String("environmentVariables".into()),
269 Yaml::Hash(env_hash),
270 );
271 }
272 }
273 }
274}
275
276pub fn log_env_var_overrides() -> Vec<String> {
278 env::vars()
279 .filter(|(k, _)| k.starts_with("FERRON_"))
280 .map(|(k, v)| format!("Environment override: {}={}", k, v))
281 .collect()
282}