Coverage Report

Created: 2024-10-22 12:33

/build/source/nativelink-util/src/tls_utils.rs
Line
Count
Source (jump to first uncovered line)
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
}