/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::{Code, Error, make_err, make_input_err}; |
17 | | use tonic::transport::Uri; |
18 | | use tracing::warn; |
19 | | |
20 | 10 | pub fn load_client_config( |
21 | 10 | config: &Option<ClientTlsConfig>, |
22 | 10 | ) -> Result<Option<tonic::transport::ClientTlsConfig>, Error> { |
23 | 10 | let Some(config9 ) = config else { Branch (23:9): [True: 9, False: 1]
Branch (23:9): [Folded - Ignored]
|
24 | 1 | return Ok(None); |
25 | | }; |
26 | | |
27 | 9 | if config.use_native_roots == Some(true) { Branch (27:8): [True: 5, False: 4]
Branch (27:8): [Folded - Ignored]
|
28 | 5 | if config.ca_file.is_some() { Branch (28:12): [True: 0, False: 5]
Branch (28:12): [Folded - Ignored]
|
29 | 0 | warn!("Native root certificates are being used, all certificate files will be ignored"); |
30 | 5 | } |
31 | 5 | return Ok(Some( |
32 | 5 | tonic::transport::ClientTlsConfig::new().with_native_roots(), |
33 | 5 | )); |
34 | 4 | } |
35 | | |
36 | 4 | let Some(ca_file3 ) = &config.ca_file else { Branch (36:9): [True: 3, False: 1]
Branch (36:9): [Folded - Ignored]
|
37 | 1 | return Err(make_err!( |
38 | 1 | Code::Internal, |
39 | 1 | "CA certificate must be provided if not using native root certificates" |
40 | 1 | )); |
41 | | }; |
42 | | |
43 | 3 | let read_config = tonic::transport::ClientTlsConfig::new().ca_certificate( |
44 | 3 | tonic::transport::Certificate::from_pem(std::fs::read_to_string(ca_file)?0 ), |
45 | | ); |
46 | 3 | let config1 = if let Some(client_certificate2 ) = &config.cert_file { Branch (46:25): [True: 2, False: 1]
Branch (46:25): [Folded - Ignored]
|
47 | 2 | let Some(client_key1 ) = &config.key_file else { Branch (47:13): [True: 1, False: 1]
Branch (47:13): [Folded - Ignored]
|
48 | 1 | return Err(make_err!( |
49 | 1 | Code::Internal, |
50 | 1 | "Client certificate specified, but no key" |
51 | 1 | )); |
52 | | }; |
53 | 1 | read_config.identity(tonic::transport::Identity::from_pem( |
54 | 1 | std::fs::read_to_string(client_certificate)?0 , |
55 | 1 | std::fs::read_to_string(client_key)?0 , |
56 | | )) |
57 | | } else { |
58 | 1 | if config.key_file.is_some() { Branch (58:12): [True: 1, False: 0]
Branch (58:12): [Folded - Ignored]
|
59 | 1 | return Err(make_err!( |
60 | 1 | Code::Internal, |
61 | 1 | "Client key specified, but no certificate" |
62 | 1 | )); |
63 | 0 | } |
64 | 0 | read_config |
65 | | }; |
66 | | |
67 | 1 | Ok(Some(config)) |
68 | 10 | } |
69 | | |
70 | 7 | pub fn endpoint_from( |
71 | 7 | endpoint: &str, |
72 | 7 | tls_config: Option<tonic::transport::ClientTlsConfig>, |
73 | 7 | ) -> Result<tonic::transport::Endpoint, Error> { |
74 | 7 | let endpoint6 = Uri::try_from(endpoint) |
75 | 7 | .map_err(|e| make_err!(Code::Internal1 , "Unable to parse endpoint {endpoint}: {e:?}"))?1 ; |
76 | | |
77 | | // Tonic uses the TLS configuration if the scheme is "https", so replace |
78 | | // grpcs with https. |
79 | 6 | let endpoint = if endpoint.scheme_str() == Some("grpcs") { Branch (79:23): [True: 1, False: 5]
Branch (79:23): [Folded - Ignored]
|
80 | 1 | let mut parts = endpoint.into_parts(); |
81 | 1 | parts.scheme = Some("https".parse().map_err(|e| {0 |
82 | 0 | make_err!( |
83 | 0 | Code::Internal, |
84 | | "https is an invalid scheme apparently? {e:?}" |
85 | | ) |
86 | 0 | })?); |
87 | 1 | parts.try_into().map_err(|e| {0 |
88 | 0 | make_err!( |
89 | 0 | Code::Internal, |
90 | | "Error changing Uri from grpcs to https: {e:?}" |
91 | | ) |
92 | 0 | })? |
93 | | } else { |
94 | 5 | endpoint |
95 | | }; |
96 | | |
97 | 6 | let endpoint_transport3 = if let Some(tls_config4 ) = tls_config { Branch (97:37): [True: 4, False: 2]
Branch (97:37): [Folded - Ignored]
|
98 | 4 | let Some(authority3 ) = endpoint.authority() else { Branch (98:13): [True: 3, False: 1]
Branch (98:13): [Folded - Ignored]
|
99 | 1 | return Err(make_input_err!( |
100 | 1 | "Unable to determine authority of endpoint: {endpoint}" |
101 | 1 | )); |
102 | | }; |
103 | 3 | if endpoint.scheme_str() != Some("https") { Branch (103:12): [True: 1, False: 2]
Branch (103:12): [Folded - Ignored]
|
104 | 1 | return Err(make_input_err!( |
105 | 1 | "You have set TLS configuration on {endpoint}, but the scheme is not https or grpcs" |
106 | 1 | )); |
107 | 2 | } |
108 | 2 | let tls_config = tls_config.domain_name(authority.host()); |
109 | 2 | tonic::transport::Endpoint::from(endpoint) |
110 | 2 | .tls_config(tls_config) |
111 | 2 | .map_err(|e| make_input_err!("Setting mTLS configuration: {e:?}"))?0 |
112 | | } else { |
113 | 2 | if endpoint.scheme_str() == Some("https") { Branch (113:12): [True: 1, False: 1]
Branch (113:12): [Folded - Ignored]
|
114 | 1 | return Err(make_input_err!( |
115 | 1 | "The scheme of {endpoint} is https or grpcs, but no TLS configuration was provided" |
116 | 1 | )); |
117 | 1 | } |
118 | 1 | tonic::transport::Endpoint::from(endpoint) |
119 | | }; |
120 | | |
121 | 3 | Ok(endpoint_transport) |
122 | 7 | } |
123 | | |
124 | 0 | pub fn endpoint(endpoint_config: &GrpcEndpoint) -> Result<tonic::transport::Endpoint, Error> { |
125 | 0 | let endpoint = endpoint_from( |
126 | 0 | &endpoint_config.address, |
127 | 0 | load_client_config(&endpoint_config.tls_config)?, |
128 | 0 | )?; |
129 | 0 | if let Some(concurrency_limit) = endpoint_config.concurrency_limit { Branch (129:12): [True: 0, False: 0]
Branch (129:12): [Folded - Ignored]
|
130 | 0 | Ok(endpoint.concurrency_limit(concurrency_limit)) |
131 | | } else { |
132 | 0 | Ok(endpoint) |
133 | | } |
134 | 0 | } |