v0.25.1
This commit is contained in:
1 parent
87e571c170
commit
fa7abc9e53
308 files changed
+49746
-50237
No files matched your search
@@ -0,0 +1,372 @@
|
||||
#[tokio::test]
|
||||
async fn csrf_required_and_logout_revokes_cookie() {
|
||||
let (router, store, _dir, secret, csrf) = fixture().await;
|
||||
let cookie = format!("{}={secret}", auth::COOKIE_NAME);
|
||||
let req = request("/api/auth/logout")
|
||||
.method("POST")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
router.clone().oneshot(req).await.unwrap().status(),
|
||||
StatusCode::FORBIDDEN
|
||||
);
|
||||
let req = request("/api/auth/logout")
|
||||
.method("POST")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", csrf)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
router.clone().oneshot(req).await.unwrap().status(),
|
||||
StatusCode::OK
|
||||
);
|
||||
let req = request("/api/status")
|
||||
.header("cookie", &cookie)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
router.oneshot(req).await.unwrap().status(),
|
||||
StatusCode::UNAUTHORIZED
|
||||
);
|
||||
store.close().await;
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn upload_and_scoped_queries_keep_control_plane_guards() {
|
||||
let (router, store, _dir, secret, csrf) = fixture().await;
|
||||
let cookie = format!("{}={secret}", auth::COOKIE_NAME);
|
||||
for (path, body) in [
|
||||
(
|
||||
"/api/certificates/upload",
|
||||
r#"{"cert_pem":"test","key_pem":"test","hosts":["app.localhost"]}"#,
|
||||
),
|
||||
(
|
||||
"/api/certificates/deploy",
|
||||
r#"{"certificate_id":"shared","cert_pem":"test","key_pem":"test"}"#,
|
||||
),
|
||||
] {
|
||||
for (authenticated, valid_csrf, expected) in
|
||||
[(false, false, 401), (true, false, 403), (true, true, 409)]
|
||||
{
|
||||
let mut req = request(path)
|
||||
.method("POST")
|
||||
.header("content-type", "application/json")
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1");
|
||||
if authenticated {
|
||||
req = req.header("cookie", &cookie);
|
||||
}
|
||||
if valid_csrf {
|
||||
req = req.header("x-csrf-token", &csrf);
|
||||
}
|
||||
assert_eq!(
|
||||
router
|
||||
.clone()
|
||||
.oneshot(req.body(Body::from(body)).unwrap())
|
||||
.await
|
||||
.unwrap()
|
||||
.status()
|
||||
.as_u16(),
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
for (path, expected) in [
|
||||
("/api/logs?vhost=demo&limit=8", 200),
|
||||
("/api/logs?cache=BAD", 400),
|
||||
("/api/traffic", 200),
|
||||
("/api/cache", 200),
|
||||
("/api/transform", 200),
|
||||
] {
|
||||
let req = request(path)
|
||||
.header("cookie", &cookie)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
router.clone().oneshot(req).await.unwrap().status().as_u16(),
|
||||
expected
|
||||
);
|
||||
}
|
||||
let req = request("/api/cache/purge?vhost=missing")
|
||||
.method("POST")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", &csrf)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
router.oneshot(req).await.unwrap().status(),
|
||||
StatusCode::NOT_FOUND
|
||||
);
|
||||
store.close().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn certificate_reload_keeps_configuration_revision_stable() {
|
||||
let (router, store, _dir, secret, csrf) = fixture().await;
|
||||
let cookie = format!("{}={secret}", auth::COOKIE_NAME);
|
||||
let response = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificates/reload")
|
||||
.method("POST")
|
||||
.header("content-type", "application/json")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", &csrf)
|
||||
.body(Body::from("{}"))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let payload = json_body(response).await;
|
||||
assert_eq!(payload["reloaded"], true);
|
||||
assert_eq!(payload["revision"], 0);
|
||||
|
||||
let configuration = router
|
||||
.oneshot(
|
||||
request("/api/config")
|
||||
.header("cookie", &cookie)
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(json_body(configuration).await["revision"], 0);
|
||||
store.close().await;
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn manual_certificate_can_be_reloaded_by_id_without_revision_change() {
|
||||
let mut config = Config::default();
|
||||
config.tls.certificates.push(crate::tls::Certificate {
|
||||
issuer: None,
|
||||
id: "manual-file".into(),
|
||||
covered_hosts: Vec::new(),
|
||||
hosts: Vec::new(),
|
||||
cert_file: "default.pem".into(),
|
||||
key_file: "default.key".into(),
|
||||
});
|
||||
let (router, store, _dir, secret, csrf) = fixture_with_certificate_storage(config).await;
|
||||
let cookie = format!("{}={secret}", auth::COOKIE_NAME);
|
||||
let response = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificates/manual-file/reload")
|
||||
.method("POST")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", &csrf)
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from("{}"))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let payload = json_body(response).await;
|
||||
assert_eq!(payload["certificate_id"], "manual-file");
|
||||
assert_eq!(payload["revision"], 0);
|
||||
assert_eq!(payload["reloaded"], true);
|
||||
|
||||
let api_token = "c".repeat(64);
|
||||
store
|
||||
.upsert_certificate_api_credential(
|
||||
"file-renewer",
|
||||
&crate::auth::secret_hash(&api_token),
|
||||
"external file renewal hook",
|
||||
"owner",
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let external = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificates/manual-file/reload")
|
||||
.method("POST")
|
||||
.header("authorization", format!("Bearer {api_token}"))
|
||||
.header("x-rustproxy-certificate-key", "file-renewer")
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from("{}"))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(external.status(), StatusCode::OK);
|
||||
assert_eq!(json_body(external).await["certificate_id"], "manual-file");
|
||||
store.close().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn certificate_api_tokens_are_scoped_commented_and_individually_revocable() {
|
||||
let (router, store, _dir, secret, csrf) = fixture().await;
|
||||
let cookie = format!("{}={secret}", auth::COOKIE_NAME);
|
||||
let token = "a".repeat(64);
|
||||
let token_two = "b".repeat(64);
|
||||
let invalid_key = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificate-api")
|
||||
.method("PUT")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", &csrf)
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(json!({"key_id":"x","token":token}).to_string()))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(invalid_key.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
||||
|
||||
for (key_id, value, comment) in [
|
||||
("certbot-main", token.as_str(), "production certbot"),
|
||||
("deploy-hook", token_two.as_str(), "secondary deploy hook"),
|
||||
] {
|
||||
let rotated = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificate-api")
|
||||
.method("PUT")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", &csrf)
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
json!({"key_id":key_id,"token":value,"comment":comment}).to_string(),
|
||||
))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(rotated.status(), StatusCode::OK);
|
||||
let rotated = json_body(rotated).await;
|
||||
assert_eq!(rotated["configured"], true);
|
||||
assert_eq!(rotated["credential"]["key_id"], key_id);
|
||||
assert_eq!(rotated["credential"]["comment"], comment);
|
||||
assert_eq!(rotated["token"], value);
|
||||
}
|
||||
|
||||
let status = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificate-api")
|
||||
.header("cookie", &cookie)
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let status = json_body(status).await;
|
||||
assert_eq!(status["configured"], true);
|
||||
assert_eq!(status["credentials"].as_array().unwrap().len(), 2);
|
||||
assert!(status.get("token").is_none());
|
||||
|
||||
let external = |key_id: &'static str, value: String| {
|
||||
request("/api/certificates/reload")
|
||||
.method("POST")
|
||||
.header("authorization", format!("Bearer {value}"))
|
||||
.header("x-rustproxy-certificate-key", key_id)
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from("{}"))
|
||||
.unwrap()
|
||||
};
|
||||
let response = router
|
||||
.clone()
|
||||
.oneshot(external("certbot-main", token.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let response = router
|
||||
.clone()
|
||||
.oneshot(external("deploy-hook", token_two.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let missing_key = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificates/reload")
|
||||
.method("POST")
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from("{}"))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(missing_key.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
let forbidden_scope = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/config")
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("x-rustproxy-certificate-key", "certbot-main")
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(forbidden_scope.status(), StatusCode::UNAUTHORIZED);
|
||||
|
||||
let revoked = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificate-api/certbot-main")
|
||||
.method("DELETE")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", &csrf)
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(revoked.status(), StatusCode::OK);
|
||||
let response = router
|
||||
.clone()
|
||||
.oneshot(external("certbot-main", token.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
let response = router
|
||||
.clone()
|
||||
.oneshot(external("deploy-hook", token_two.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let revoked_all = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
request("/api/certificate-api")
|
||||
.method("DELETE")
|
||||
.header("cookie", &cookie)
|
||||
.header("origin", "http://127.0.0.1:9090")
|
||||
.header("x-rustproxy-request", "1")
|
||||
.header("x-csrf-token", &csrf)
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(revoked_all.status(), StatusCode::OK);
|
||||
let response = router
|
||||
.oneshot(external("deploy-hook", token_two))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
store.close().await;
|
||||
}
|
||||
Reference in new issue
Block a user