@@ -31,6 +31,8 @@ struct Options {
31
31
// key: PathBuf,
32
32
}
33
33
34
+ const NGINX_CONF : & str = "nginx.conf.jbl" ;
35
+
34
36
#[ tokio:: main]
35
37
async fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
36
38
init_crypto ( ) ;
@@ -69,13 +71,15 @@ A Rust load balancer for Jonline servers deployed on Kubernetes
69
71
return Ok ( ( ) ) ;
70
72
} else {
71
73
log:: info!( "Starting NGINX..." ) ;
72
- let _nginx = match std:: process:: Command :: new ( "nginx" )
73
- . args ( & [ "-c" , "nginx.conf.jbl" , "-p" , & pwd] )
74
+ let mut nginx = match std:: process:: Command :: new ( "nginx" )
75
+ . args ( & [ "-c" , NGINX_CONF , "-p" , & pwd, "-g" , "daemon off;" ] )
74
76
. spawn ( )
75
77
{
76
78
Ok ( process) => process,
77
79
Err ( err) => panic ! ( "Nginx crashed: {}" , err) ,
78
80
} ;
81
+
82
+ nginx. wait ( ) . expect ( "Nginx crashed" ) ;
79
83
}
80
84
81
85
Ok ( ( ) )
@@ -121,10 +125,12 @@ async fn setup_nginx_config(_options: &Options) -> io::Result<JonlineServerConfi
121
125
122
126
let env_servers = env:: var ( "SERVERS" )
123
127
. expect ( "SERVERS must be set, JSON of the format [{\" host\" : \" \" , \" namespace\" : \" \" }]" ) ;
128
+ let env_no_certs = env:: var ( "NO_CERTS" ) . unwrap_or ( "false" . to_string ( ) ) ;
124
129
// TODO:
125
130
log:: info!( "SERVERS={:?}" , env_servers) ;
126
131
let servers: Vec < Server > = serde_json:: from_str ( & env_servers)
127
132
. expect ( format ! ( "Invalid SERVERS JSON: {}" , env_servers) . as_str ( ) ) ;
133
+ let no_certs = env_no_certs == "true" ;
128
134
log:: info!( "Parsed servers: {:?}" , & servers) ;
129
135
130
136
// Source: https://stackoverflow.com/questions/73418121/how-allow-pod-from-default-namespace-read-secret-from-other-namespace/73419051#73419051
@@ -160,122 +166,146 @@ async fn setup_nginx_config(_options: &Options) -> io::Result<JonlineServerConfi
160
166
)
161
167
} ) ?;
162
168
163
- let nginx_conf = "nginx.conf.jbl" ;
164
- fs :: write ( nginx_conf , "events {} ") ?;
169
+ fs :: write ( NGINX_CONF , "events {}" ) ? ;
170
+ append_to_conf ( "http { ") ?;
165
171
for server in & servers {
166
172
let host = & server. host ;
167
173
let namespace = & server. namespace ;
168
174
169
- log:: info!( "Fetching certs for server {host} in namespace {namespace}..." ) ;
170
- let secrets_url = format ! (
171
- "https://{}:{}/api/v1/namespaces/{}/secrets" ,
172
- k8s_server_host, k8s_server_port, & server. namespace
173
- ) ;
174
-
175
- // THE LOG BELOW SHOULD DEFINITELY NOT RUN IN REAL PRODUCTION ENVIRIONMENTS (but is needed to test security internally)
176
- // log::info!("Secrets URL: {:?}", secrets_url);
177
- // log::info!("K8s Secrets Token: {:?}", k8s_token);
178
-
179
- let secrets = client
180
- . get ( secrets_url)
181
- . send ( )
182
- . await
183
- . map_err ( |e| {
184
- log:: error!( "Failed to fetch secrets: {:?}" , e) ;
185
- io:: Error :: new (
186
- io:: ErrorKind :: InvalidInput ,
187
- format ! ( "Failed to fetch secrets: {:?}" , e) ,
188
- )
189
- } ) ?
190
- . text ( )
191
- . await
192
- . map_err ( |e| {
193
- log:: error!( "Failed to read secrets: {:?}" , e) ;
175
+ if no_certs {
176
+ log:: info!( "Skipping fetching certs for server {host} in namespace {namespace}..." ) ;
177
+
178
+ append_to_conf ( & format ! (
179
+ "
180
+ server {{
181
+ listen 80;
182
+ server_name {host};
183
+ location / {{
184
+ proxy_pass https://{host}:80/;
185
+ proxy_http_version 1.1;
186
+ proxy_set_header Upgrade $http_upgrade;
187
+ proxy_set_header Connection 'upgrade';
188
+ proxy_set_header Host $host;
189
+ proxy_cache_bypass $http_upgrade;
190
+ }}
191
+ }}
192
+ "
193
+ ) ) ?;
194
+ } else {
195
+ log:: info!( "Fetching certs for server {host} in namespace {namespace}..." ) ;
196
+ let secrets_url = format ! (
197
+ "https://{}:{}/api/v1/namespaces/{}/secrets" ,
198
+ k8s_server_host, k8s_server_port, & server. namespace
199
+ ) ;
200
+
201
+ // THE LOG BELOW SHOULD DEFINITELY NOT RUN IN REAL PRODUCTION ENVIRIONMENTS (but is needed to test security internally)
202
+ // log::info!("Secrets URL: {:?}", secrets_url);
203
+ // log::info!("K8s Secrets Token: {:?}", k8s_token);
204
+
205
+ let secrets = client
206
+ . get ( secrets_url)
207
+ . send ( )
208
+ . await
209
+ . map_err ( |e| {
210
+ log:: error!( "Failed to fetch secrets: {:?}" , e) ;
211
+ io:: Error :: new (
212
+ io:: ErrorKind :: InvalidInput ,
213
+ format ! ( "Failed to fetch secrets: {:?}" , e) ,
214
+ )
215
+ } ) ?
216
+ . text ( )
217
+ . await
218
+ . map_err ( |e| {
219
+ log:: error!( "Failed to read secrets: {:?}" , e) ;
220
+ io:: Error :: new (
221
+ io:: ErrorKind :: InvalidInput ,
222
+ format ! ( "Failed to read secrets: {:?}" , e) ,
223
+ )
224
+ } ) ?;
225
+ // log::debug!("Secrets response for server {:?}: {:?}", server, secrets);
226
+
227
+ let parsed_secrets =
228
+ serde_json:: from_str :: < KubernetesSecrets > ( & secrets) . map_err ( |e| {
229
+ log:: error!( "Failed to parse secrets: {:?}" , e) ;
230
+ io:: Error :: new (
231
+ io:: ErrorKind :: InvalidInput ,
232
+ format ! ( "Failed to parse secrets: {:?}" , e) ,
233
+ )
234
+ } ) ?;
235
+
236
+ let tls_secrets = parsed_secrets
237
+ . items
238
+ . iter ( )
239
+ . filter ( |item| item. metadata . name == "jonline-generated-tls" )
240
+ . next ( )
241
+ . ok_or_else ( || {
242
+ log:: error!( "Failed to find tls secret for server: {:?}" , server) ;
243
+ io:: Error :: new (
244
+ io:: ErrorKind :: InvalidInput ,
245
+ format ! ( "Failed to find tls secret for server: {:?}" , server) ,
246
+ )
247
+ } ) ?;
248
+
249
+ log:: info!(
250
+ "Parsed TLS secrets for server {:?}: {:?}" ,
251
+ server,
252
+ tls_secrets
253
+ ) ;
254
+
255
+ let cert = tls_secrets. data . get ( "tls.crt" ) . ok_or_else ( || {
256
+ log:: error!( "Failed to find tls.crt for server: {:?}" , server) ;
194
257
io:: Error :: new (
195
258
io:: ErrorKind :: InvalidInput ,
196
- format ! ( "Failed to read secrets : {:?}" , e ) ,
259
+ format ! ( "Failed to find tls.crt for server : {:?}" , server ) ,
197
260
)
198
261
} ) ?;
199
- // log::debug!("Secrets response for server {:?}: {:?}", server, secrets);
200
262
201
- let parsed_secrets = serde_json:: from_str :: < KubernetesSecrets > ( & secrets) . map_err ( |e| {
202
- log:: error!( "Failed to parse secrets: {:?}" , e) ;
203
- io:: Error :: new (
204
- io:: ErrorKind :: InvalidInput ,
205
- format ! ( "Failed to parse secrets: {:?}" , e) ,
206
- )
207
- } ) ?;
208
-
209
- let tls_secrets = parsed_secrets
210
- . items
211
- . iter ( )
212
- . filter ( |item| item. metadata . name == "jonline-generated-tls" )
213
- . next ( )
214
- . ok_or_else ( || {
215
- log:: error!( "Failed to find tls secret for server: {:?}" , server) ;
263
+ let key = tls_secrets. data . get ( "tls.key" ) . ok_or_else ( || {
264
+ log:: error!( "Failed to find tls.key for server: {:?}" , server) ;
216
265
io:: Error :: new (
217
266
io:: ErrorKind :: InvalidInput ,
218
- format ! ( "Failed to find tls secret for server: {:?}" , server) ,
267
+ format ! ( "Failed to find tls.key for server: {:?}" , server) ,
219
268
)
220
269
} ) ?;
221
270
222
- log:: info!(
223
- "Parsed TLS secrets for server {:?}: {:?}" ,
224
- server,
225
- tls_secrets
226
- ) ;
227
-
228
- let cert = tls_secrets. data . get ( "tls.crt" ) . ok_or_else ( || {
229
- log:: error!( "Failed to find tls.crt for server: {:?}" , server) ;
230
- io:: Error :: new (
231
- io:: ErrorKind :: InvalidInput ,
232
- format ! ( "Failed to find tls.crt for server: {:?}" , server) ,
233
- )
234
- } ) ?;
235
-
236
- let key = tls_secrets. data . get ( "tls.key" ) . ok_or_else ( || {
237
- log:: error!( "Failed to find tls.key for server: {:?}" , server) ;
238
- io:: Error :: new (
239
- io:: ErrorKind :: InvalidInput ,
240
- format ! ( "Failed to find tls.key for server: {:?}" , server) ,
241
- )
242
- } ) ?;
243
-
244
- let cert_file = & format ! ( "{host}.crt.jbl" ) ;
245
- fs:: write ( cert_file, cert) ?;
246
- let key_file = & format ! ( "{host}.key.jbl" ) ;
247
- fs:: write ( key_file, key) ?;
248
-
249
- let mut file = fs:: OpenOptions :: new ( )
250
- . write ( true )
251
- . append ( true )
252
- . open ( nginx_conf)
253
- . unwrap ( ) ;
254
-
255
- if let Err ( e) = writeln ! (
256
- file,
257
- "
258
- server {{
259
- listen 443 ssl;
260
- server_name {host};
261
- ssl on;
262
- ssl_certificate {cert_file};
263
- ssl_certificate_key {key_file};
264
- include /etc/letsencrypt/options-ssl-nginx.conf;
265
- location / {{
266
- proxy_pass https://{host}:80/;
267
- proxy_http_version 1.1;
268
- proxy_set_header Upgrade $http_upgrade;
269
- proxy_set_header Connection 'upgrade';
270
- proxy_set_header Host $host;
271
- proxy_cache_bypass $http_upgrade;
271
+ let cert_file = & format ! ( "{host}.crt.jbl" ) ;
272
+ fs:: write ( cert_file, cert) ?;
273
+ let key_file = & format ! ( "{host}.key.jbl" ) ;
274
+ fs:: write ( key_file, key) ?;
275
+
276
+ append_to_conf ( & format ! (
277
+ "
278
+ server {{
279
+ listen 443 ssl;
280
+ server_name {host};
281
+ ssl on;
282
+ ssl_certificate {cert_file};
283
+ ssl_certificate_key {key_file};
284
+ include /etc/letsencrypt/options-ssl-nginx.conf;
285
+ location / {{
286
+ proxy_pass https://{host}:80/;
287
+ proxy_http_version 1.1;
288
+ proxy_set_header Upgrade $http_upgrade;
289
+ proxy_set_header Connection 'upgrade';
290
+ proxy_set_header Host $host;
291
+ proxy_cache_bypass $http_upgrade;
292
+ }}
272
293
}}
273
- }}
274
294
"
275
- ) {
276
- eprintln ! ( "Couldn't write to NGINX config file: {}" , e) ;
295
+ ) ) ?;
277
296
}
278
297
}
279
298
299
+ append_to_conf ( "}" ) ?;
300
+
280
301
Ok ( JonlineServerConfig { servers } )
281
302
}
303
+
304
+ fn append_to_conf ( content : & str ) -> io:: Result < ( ) > {
305
+ let mut file = fs:: OpenOptions :: new ( )
306
+ . write ( true )
307
+ . append ( true )
308
+ . open ( NGINX_CONF )
309
+ . unwrap ( ) ;
310
+ writeln ! ( file, "\n {}" , content)
311
+ }
0 commit comments