/build/source/nativelink-util/src/tls_utils.rs
Line | Count | Source |
1 | | // Copyright 2024 The NativeLink Authors. All rights reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | use nativelink_config::stores::{ClientTlsConfig, GrpcEndpoint}; |
16 | | use nativelink_error::{make_err, make_input_err, Code, Error}; |
17 | | use tonic::transport::Uri; |
18 | | |
19 | 0 | pub fn load_client_config( |
20 | 0 | config: &Option<ClientTlsConfig>, |
21 | 0 | ) -> Result<Option<tonic::transport::ClientTlsConfig>, Error> { |
22 | 0 | let Some(config) = config else { Branch (22:9): [True: 0, False: 0]
Branch (22:9): [Folded - Ignored]
|
23 | 0 | return Ok(None); |
24 | | }; |
25 | | |
26 | 0 | let read_config = tonic::transport::ClientTlsConfig::new().ca_certificate( |
27 | 0 | tonic::transport::Certificate::from_pem(std::fs::read_to_string(&config.ca_file)?), |
28 | | ); |
29 | 0 | let config = if let Some(client_certificate) = &config.cert_file { Branch (29:25): [True: 0, False: 0]
Branch (29:25): [Folded - Ignored]
|
30 | 0 | let Some(client_key) = &config.key_file else { Branch (30:13): [True: 0, False: 0]
Branch (30:13): [Folded - Ignored]
|
31 | 0 | return Err(make_err!( |
32 | 0 | Code::Internal, |
33 | 0 | "Client certificate specified, but no key" |
34 | 0 | )); |
35 | | }; |
36 | 0 | read_config.identity(tonic::transport::Identity::from_pem( |
37 | 0 | std::fs::read_to_string(client_certificate)?, |
38 | 0 | std::fs::read_to_string(client_key)?, |
39 | | )) |
40 | | } else { |
41 | 0 | if config.key_file.is_some() { Branch (41:12): [True: 0, False: 0]
Branch (41:12): [Folded - Ignored]
|
42 | 0 | return Err(make_err!( |
43 | 0 | Code::Internal, |
44 | 0 | "Client key specified, but no certificate" |
45 | 0 | )); |
46 | 0 | } |
47 | 0 | read_config |
48 | | }; |
49 | | |
50 | 0 | Ok(Some(config)) |
51 | 0 | } |
52 | | |
53 | 0 | pub fn endpoint_from( |
54 | 0 | endpoint: &str, |
55 | 0 | tls_config: Option<tonic::transport::ClientTlsConfig>, |
56 | 0 | ) -> Result<tonic::transport::Endpoint, Error> { |
57 | 0 | let endpoint = Uri::try_from(endpoint) |
58 | 0 | .map_err(|e| make_err!(Code::Internal, "Unable to parse endpoint {endpoint}: {e:?}"))?; |
59 | | |
60 | | // Tonic uses the TLS configuration if the scheme is "https", so replace |
61 | | // grpcs with https. |
62 | 0 | let endpoint = if endpoint.scheme_str() == Some("grpcs") { Branch (62:23): [True: 0, False: 0]
Branch (62:23): [Folded - Ignored]
|
63 | 0 | let mut parts = endpoint.into_parts(); |
64 | 0 | parts.scheme = Some("https".parse().map_err(|e| { |
65 | 0 | make_err!( |
66 | 0 | Code::Internal, |
67 | 0 | "https is an invalid scheme apparently? {e:?}" |
68 | 0 | ) |
69 | 0 | })?); |
70 | 0 | parts.try_into().map_err(|e| { |
71 | 0 | make_err!( |
72 | 0 | Code::Internal, |
73 | 0 | "Error changing Uri from grpcs to https: {e:?}" |
74 | 0 | ) |
75 | 0 | })? |
76 | | } else { |
77 | 0 | endpoint |
78 | | }; |
79 | | |
80 | 0 | let endpoint_transport = if let Some(tls_config) = tls_config { Branch (80:37): [True: 0, False: 0]
Branch (80:37): [Folded - Ignored]
|
81 | 0 | let Some(authority) = endpoint.authority() else { Branch (81:13): [True: 0, False: 0]
Branch (81:13): [Folded - Ignored]
|
82 | 0 | return Err(make_input_err!( |
83 | 0 | "Unable to determine authority of endpont: {endpoint}" |
84 | 0 | )); |
85 | | }; |
86 | 0 | if endpoint.scheme_str() != Some("https") { Branch (86:12): [True: 0, False: 0]
Branch (86:12): [Folded - Ignored]
|
87 | 0 | return Err(make_input_err!( |
88 | 0 | "You have set TLS configuration on {endpoint}, but the scheme is not https or grpcs" |
89 | 0 | )); |
90 | 0 | } |
91 | 0 | let tls_config = tls_config.domain_name(authority.host()); |
92 | 0 | tonic::transport::Endpoint::from(endpoint) |
93 | 0 | .tls_config(tls_config) |
94 | 0 | .map_err(|e| make_input_err!("Setting mTLS configuration: {e:?}"))? |
95 | | } else { |
96 | 0 | tonic::transport::Endpoint::from(endpoint) |
97 | | }; |
98 | | |
99 | 0 | Ok(endpoint_transport) |
100 | 0 | } |
101 | | |
102 | 0 | pub fn endpoint(endpoint_config: &GrpcEndpoint) -> Result<tonic::transport::Endpoint, Error> { |
103 | 0 | let endpoint = endpoint_from( |
104 | 0 | &endpoint_config.address, |
105 | 0 | load_client_config(&endpoint_config.tls_config)?, |
106 | 0 | )?; |
107 | 0 | if let Some(concurrency_limit) = endpoint_config.concurrency_limit { Branch (107:12): [True: 0, False: 0]
Branch (107:12): [Folded - Ignored]
|
108 | 0 | Ok(endpoint.concurrency_limit(concurrency_limit)) |
109 | | } else { |
110 | 0 | Ok(endpoint) |
111 | | } |
112 | 0 | } |