Skip to main content

mz_sql_server_util/
config.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use anyhow::Context;
11use std::net::SocketAddr;
12
13use mz_ore::future::InTask;
14use mz_repr::CatalogItemId;
15use mz_ssh_util::tunnel::{SshTimeoutConfig, SshTunnelConfig};
16use mz_ssh_util::tunnel_manager::SshTunnelManager;
17use proptest_derive::Arbitrary;
18use serde::{Deserialize, Serialize};
19
20/// Materialize specific configuration for SQL Server connections.
21///
22/// This wraps a [`tiberius::Config`] so we can configure a tunnel over SSH, AWS
23/// PrivateLink, or various other techniques, via a [`TunnelConfig`]
24#[derive(Clone, Debug)]
25pub struct Config {
26    /// SQL Server specific configuration.
27    pub(crate) inner: tiberius::Config,
28    /// Details of how we'll connect to the upstream SQL Server instance.
29    pub(crate) tunnel: TunnelConfig,
30    /// If all of the I/O for this connection will be done in a separate task.
31    ///
32    /// Note: This is used to prevent accidentally doing I/O in timely threads.
33    pub(crate) in_task: InTask,
34}
35
36impl Config {
37    pub fn new(inner: tiberius::Config, tunnel: TunnelConfig, in_task: InTask) -> Self {
38        Config {
39            inner,
40            tunnel,
41            in_task,
42        }
43    }
44
45    /// Create a new [`Config`] from an ActiveX Data Object.
46    ///
47    /// Generally this is only used in test environments, see [`Config::new`]
48    /// for regular/production use cases.
49    pub fn from_ado_string(s: &str) -> Result<Self, anyhow::Error> {
50        let inner = tiberius::Config::from_ado_string(s).context("tiberius config")?;
51        Ok(Config {
52            inner,
53            tunnel: TunnelConfig::Direct {
54                resolved_addresses: Default::default(),
55            },
56            in_task: InTask::No,
57        })
58    }
59}
60
61/// Configures an optional tunnel for use when connecting to a SQL Server database.
62///
63/// TODO(sql_server2): De-duplicate this with MySQL and Postgres sources.
64#[derive(Debug, Clone)]
65pub enum TunnelConfig {
66    /// Establish a direct TCP connection to the database host.
67    /// If `resolved_addresses` is non-empty, those addresses are used directly;
68    /// otherwise falls back to DNS lookup of the host.
69    Direct {
70        resolved_addresses: Box<[SocketAddr]>,
71    },
72    /// Establish a TCP connection to the database via an SSH tunnel.
73    Ssh {
74        /// Config for opening the SSH tunnel.
75        config: SshTunnelConfig,
76        /// Global manager of SSH tunnels.
77        manager: SshTunnelManager,
78        /// Timeout config for the SSH tunnel.
79        timeout: SshTimeoutConfig,
80        // TODO(sql_server3): Remove these fields by forking the `tiberius`
81        // crate and expose the `get_host` and `get_port` methods.
82        //
83        // See: <https://github.com/MaterializeInc/tiberius/blob/406ad2780d206617bd41689b1b638bddf4538f89/src/client/config.rs#L174-L191>
84        host: String,
85        port: u16,
86    },
87    /// Establish a TCP connection to the database via an AWS PrivateLink service.
88    AwsPrivatelink {
89        /// The ID of the AWS PrivateLink service.
90        connection_id: CatalogItemId,
91        port: u16,
92    },
93}
94
95/// Level of encryption to use with a SQL Server connection.
96///
97/// Mirror of [`tiberius::EncryptionLevel`] but we define our own so we can
98/// implement traits like [`Serialize`].
99#[derive(
100    Copy,
101    Clone,
102    Debug,
103    PartialEq,
104    Eq,
105    Hash,
106    Arbitrary,
107    Serialize,
108    Deserialize
109)]
110pub enum EncryptionLevel {
111    /// Do not use encryption at all.
112    None,
113    /// Only use encryption for the login procedure.
114    Login,
115    /// Use encryption for everything, if possible.
116    Preferred,
117    /// Require encryption, failing if not possible.
118    Required,
119}
120
121impl From<tiberius::EncryptionLevel> for EncryptionLevel {
122    fn from(value: tiberius::EncryptionLevel) -> Self {
123        match value {
124            tiberius::EncryptionLevel::NotSupported => EncryptionLevel::None,
125            tiberius::EncryptionLevel::Off => EncryptionLevel::Login,
126            tiberius::EncryptionLevel::On => EncryptionLevel::Preferred,
127            tiberius::EncryptionLevel::Required => EncryptionLevel::Required,
128        }
129    }
130}
131
132impl From<EncryptionLevel> for tiberius::EncryptionLevel {
133    fn from(value: EncryptionLevel) -> Self {
134        match value {
135            EncryptionLevel::None => tiberius::EncryptionLevel::NotSupported,
136            EncryptionLevel::Login => tiberius::EncryptionLevel::Off,
137            EncryptionLevel::Preferred => tiberius::EncryptionLevel::On,
138            EncryptionLevel::Required => tiberius::EncryptionLevel::Required,
139        }
140    }
141}
142
143/// Policy that dictates validation of the SQL-SERVER certificate.
144#[derive(
145    Copy,
146    Clone,
147    Debug,
148    PartialEq,
149    Eq,
150    Hash,
151    Arbitrary,
152    Serialize,
153    Deserialize
154)]
155pub enum CertificateValidationPolicy {
156    /// Don't validate the server's certificate; trust all certificates.
157    TrustAll,
158    /// Validate server's certificate using system certificates.
159    VerifySystem,
160    /// Validate server's certifiacte using provided CA certificate.
161    VerifyCA,
162}