aboutsummaryrefslogtreecommitdiffstats
path: root/gitolfs3-server/src/config.rs
blob: 75e84dc088e9b8d80bb31f2b568b231af062f997 (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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::collections::HashSet;

use gitolfs3_common::{load_key, Key};

struct Env {
    s3_access_key_id: String,
    s3_secret_access_key: String,
    s3_bucket: String,
    s3_region: String,
    s3_endpoint: String,
    base_url: String,
    key_path: String,
    listen_host: String,
    listen_port: String,
    download_limit: String,
    trusted_forwarded_hosts: String,
}

fn require_env(name: &str) -> Result<String, String> {
    std::env::var(name)
        .map_err(|_| format!("environment variable {name} should be defined and valid"))
}

impl Env {
    fn load() -> Result<Env, String> {
        Ok(Env {
            s3_secret_access_key: require_env("GITOLFS3_S3_SECRET_ACCESS_KEY_FILE")?,
            s3_access_key_id: require_env("GITOLFS3_S3_ACCESS_KEY_ID_FILE")?,
            s3_region: require_env("GITOLFS3_S3_REGION")?,
            s3_endpoint: require_env("GITOLFS3_S3_ENDPOINT")?,
            s3_bucket: require_env("GITOLFS3_S3_BUCKET")?,
            base_url: require_env("GITOLFS3_BASE_URL")?,
            key_path: require_env("GITOLFS3_KEY_PATH")?,
            listen_host: require_env("GITOLFS3_LISTEN_HOST")?,
            listen_port: require_env("GITOLFS3_LISTEN_PORT")?,
            download_limit: require_env("GITOLFS3_DOWNLOAD_LIMIT")?,
            trusted_forwarded_hosts: std::env::var("GITOLFS3_TRUSTED_FORWARDED_HOSTS")
                .unwrap_or_default(),
        })
    }
}

fn get_s3_client(env: &Env) -> Result<aws_sdk_s3::Client, std::io::Error> {
    let access_key_id = std::fs::read_to_string(&env.s3_access_key_id)?;
    let secret_access_key = std::fs::read_to_string(&env.s3_secret_access_key)?;

    let credentials = aws_sdk_s3::config::Credentials::new(
        access_key_id,
        secret_access_key,
        None,
        None,
        "gitolfs3-env",
    );
    let config = aws_config::SdkConfig::builder()
        .behavior_version(aws_config::BehaviorVersion::latest())
        .region(aws_config::Region::new(env.s3_region.clone()))
        .endpoint_url(&env.s3_endpoint)
        .credentials_provider(aws_sdk_s3::config::SharedCredentialsProvider::new(
            credentials,
        ))
        .build();
    Ok(aws_sdk_s3::Client::new(&config))
}

pub struct Config {
    pub listen_addr: (String, u16),
    pub base_url: String,
    pub authz_conf: AuthorizationConfig,
    pub s3_client: aws_sdk_s3::Client,
    pub s3_bucket: String,
    pub download_limit: u64,
}

pub struct AuthorizationConfig {
    pub trusted_forwarded_hosts: HashSet<String>,
    pub key: Key,
}

impl Config {
    pub fn load() -> Result<Self, String> {
        let env = match Env::load() {
            Ok(env) => env,
            Err(e) => return Err(format!("failed to load configuration: {e}")),
        };

        let s3_client = match get_s3_client(&env) {
            Ok(s3_client) => s3_client,
            Err(e) => return Err(format!("failed to create S3 client: {e}")),
        };
        let key = match load_key(&env.key_path) {
            Ok(key) => key,
            Err(e) => return Err(format!("failed to load Gitolfs3 key: {e}")),
        };

        let trusted_forwarded_hosts: HashSet<String> = env
            .trusted_forwarded_hosts
            .split(',')
            .map(|s| s.to_owned())
            .filter(|s| !s.is_empty())
            .collect();
        let base_url = env.base_url.trim_end_matches('/').to_string();

        let Ok(listen_port): Result<u16, _> = env.listen_port.parse() else {
            return Err("configured GITOLFS3_LISTEN_PORT is invalid".to_string());
        };
        let Ok(download_limit): Result<u64, _> = env.download_limit.parse() else {
            return Err("configured GITOLFS3_DOWNLOAD_LIMIT is invalid".to_string());
        };

        Ok(Self {
            listen_addr: (env.listen_host, listen_port),
            base_url,
            authz_conf: AuthorizationConfig {
                key,
                trusted_forwarded_hosts,
            },
            s3_client,
            s3_bucket: env.s3_bucket,
            download_limit,
        })
    }
}