Skip to content

Commit a7a2256

Browse files
committed
Obtain allowed origin list from the environment
This adapts the scheme added in #2627. When operating behind CloudFront, the Host received by nginx is the Heroku instance and does not match the user facing crates.io origin. Therefore it is necessary to obtain a list of allowed origins at boot time. If this header is not set, the server will panic and will not boot.
1 parent d37efb6 commit a7a2256

File tree

4 files changed

+21
-21
lines changed

4 files changed

+21
-21
lines changed

.env.sample

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
# `postgres://postgres@localhost/cargo_registry`.
44
export DATABASE_URL=
55

6+
# Allowed origins - any origins for which you want to allow browser
7+
# access to authenticated endpoints.
8+
export WEB_ALLOWED_ORIGINS=http://localhost:8888,http://localhost:4200
9+
610
# If you are running a mirror of crates.io, uncomment this line.
711
# export MIRROR=1
812

src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub struct Config {
1717
pub publish_rate_limit: PublishRateLimit,
1818
pub blocked_traffic: Vec<(String, Vec<String>)>,
1919
pub domain_name: String,
20+
pub allowed_origins: Vec<String>,
2021
}
2122

2223
impl Default for Config {
@@ -121,6 +122,10 @@ impl Default for Config {
121122
}
122123
}
123124
};
125+
let allowed_origins = env("WEB_ALLOWED_ORIGINS")
126+
.split(',')
127+
.map(ToString::to_string)
128+
.collect();
124129
Config {
125130
uploader,
126131
session_key: env("SESSION_KEY"),
@@ -136,6 +141,7 @@ impl Default for Config {
136141
publish_rate_limit: Default::default(),
137142
blocked_traffic: blocked_traffic(),
138143
domain_name: domain_name(),
144+
allowed_origins,
139145
}
140146
}
141147
}

src/controllers/util.rs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::models::{ApiToken, User};
66
use crate::util::errors::{
77
forbidden, internal, AppError, AppResult, ChainError, InsecurelyGeneratedTokenRevoked,
88
};
9-
use conduit::Host;
109

1110
#[derive(Debug)]
1211
pub struct AuthenticatedUser {
@@ -36,32 +35,22 @@ impl AuthenticatedUser {
3635
// be: https://crates.io in production, or http://localhost:port/ in development.
3736
fn verify_origin(req: &dyn RequestExt) -> AppResult<()> {
3837
let headers = req.headers();
39-
// If x-forwarded-host and -proto are present, trust those to tell us what the proto and host
40-
// are; otherwise (in local dev) trust the Host header and the scheme.
41-
let forwarded_host = headers.get("x-forwarded-host");
42-
let forwarded_proto = headers.get("x-forwarded-proto");
43-
let expected_origin = match (forwarded_host, forwarded_proto) {
44-
(Some(host), Some(proto)) => format!(
45-
"{}://{}",
46-
proto.to_str().unwrap_or_default(),
47-
host.to_str().unwrap_or_default()
48-
),
49-
// For the default case we assume HTTP, because we know we're not serving behind a reverse
50-
// proxy, and we also know that crates by itself doesn't serve HTTPS.
51-
_ => match req.host() {
52-
Host::Name(a) => format!("http://{}", a),
53-
Host::Socket(a) => format!("http://{}", a.to_string()),
54-
},
55-
};
38+
let allowed_origins = req
39+
.app()
40+
.config
41+
.allowed_origins
42+
.iter()
43+
.map(|s| &**s)
44+
.collect::<Vec<_>>();
5645

5746
let bad_origin = headers
5847
.get_all(header::ORIGIN)
5948
.iter()
60-
.find(|h| h.to_str().unwrap_or_default() != expected_origin);
49+
.find(|h| !allowed_origins.contains(&h.to_str().unwrap_or_default()));
6150
if let Some(bad_origin) = bad_origin {
6251
let error_message = format!(
63-
"only same-origin requests can be authenticated. expected {}, got {:?}",
64-
expected_origin, bad_origin
52+
"only same-origin requests can be authenticated. got {:?}",
53+
bad_origin
6554
);
6655
return Err(internal(&error_message))
6756
.chain_error(|| Box::new(forbidden()) as Box<dyn AppError>);

src/tests/all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ fn simple_config() -> Config {
145145
publish_rate_limit: Default::default(),
146146
blocked_traffic: Default::default(),
147147
domain_name: "crates.io".into(),
148+
allowed_origins: Vec::new(),
148149
}
149150
}
150151

0 commit comments

Comments
 (0)