summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 583f10620227defbc86265296bd54dbfa03c4a10 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
pub mod catalog;
mod kv;
pub mod locking;
mod with_index;

use std::fs::File;
use std::io::Read;

use anyhow::{bail, Result};

pub use with_index::WithIndex;

/// Configuration parameters to talk to a Consul server
pub struct Config {
    /// HTTP address of the Consul server, with `http://` or `https://` prefix
    pub addr: String,
    /// CA certificate of the Consul CA, when using TLS
    pub ca_cert: Option<String>,
    /// Client certificate for client auth when using TLS
    pub client_cert: Option<String>,
    /// Client key for client auth when using TLS
    pub client_key: Option<String>,
    /// Skip verification of consul server TLS certificates
    pub tls_skip_verify: bool,
}

/// Client used to talk to a Consul server.
/// All calls to the key/value API are automatically prefixed with an arbitrary string
/// that is constructed at client creation.
#[derive(Clone)]
pub struct Consul {
    client: reqwest::Client,

    url: String,
    kv_prefix: String,
}

impl Consul {
    pub fn new(config: Config, kv_prefix: &str) -> Result<Self> {
        let client = match (&config.client_cert, &config.client_key) {
            (Some(client_cert), Some(client_key)) => {
                let mut client_cert_buf = vec![];
                File::open(client_cert)?.read_to_end(&mut client_cert_buf)?;

                let mut client_key_buf = vec![];
                File::open(client_key)?.read_to_end(&mut client_key_buf)?;

                let identity = reqwest::Identity::from_pem(
                    &[&client_cert_buf[..], &client_key_buf[..]].concat()[..],
                )?;

                if config.tls_skip_verify {
                    reqwest::Client::builder()
                        .use_rustls_tls()
                        .danger_accept_invalid_certs(true)
                        .identity(identity)
                        .build()?
                } else if let Some(ca_cert) = &config.ca_cert {
                    let mut ca_cert_buf = vec![];
                    File::open(ca_cert)?.read_to_end(&mut ca_cert_buf)?;

                    reqwest::Client::builder()
                        .use_rustls_tls()
                        .add_root_certificate(reqwest::Certificate::from_pem(&ca_cert_buf[..])?)
                        .identity(identity)
                        .build()?
                } else {
                    reqwest::Client::builder()
                        .use_rustls_tls()
                        .identity(identity)
                        .build()?
                }
            }
            (None, None) => reqwest::Client::new(),
            _ => bail!("Incomplete Consul TLS configuration parameters"),
        };

        Ok(Self {
            client,
            url: config.addr.trim_end_matches('/').to_string(),
            kv_prefix: kv_prefix.to_string(),
        })
    }
}