Skip to content

Commit a80338a

Browse files
committed
Allow client crates to upload masked files
Although we already automatically mask the trace sent to Gitlab, some runners will produce additional log files or metadata, which must also be masked to prevent secrets from being revealed in the artifact data. This permits client crates to identify a file they wish to upload as additionally requiring masking.
1 parent 6cd41e5 commit a80338a

File tree

3 files changed

+69
-18
lines changed

3 files changed

+69
-18
lines changed

gitlab-runner/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ tracing-subscriber = "0.3.8"
2929
tracing = "0.1.30"
3030
doc-comment = "0.3.3"
3131
tokio-util = { version = "0.7", features = [ "io" ] }
32-
masker = "0.0.1"
32+
masker = { path = "../../masker" }
3333

3434
[dev-dependencies]
3535
tokio = { version = "1.5.0", features = [ "full", "test-util" ] }

gitlab-runner/src/run.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ async fn run<F, J, Ret>(
2222
job: Job,
2323
client: Client,
2424
response: Arc<JobResponse>,
25+
masker: Masker,
2526
process: F,
2627
build_dir: PathBuf,
2728
cancel_token: CancellationToken,
@@ -65,7 +66,7 @@ where
6566
});
6667

6768
let r = if upload {
68-
if let Ok(mut uploader) = Uploader::new(client, &build_dir, response) {
69+
if let Ok(mut uploader) = Uploader::new(client, &build_dir, response, masker) {
6970
let r = handler.upload_artifacts(&mut uploader).await;
7071
if r.is_ok() {
7172
uploader.upload().await.and(script_result)
@@ -187,6 +188,15 @@ impl Run {
187188
{
188189
let cancel_token = CancellationToken::new();
189190

191+
let masked_variables = self
192+
.response
193+
.variables
194+
.iter()
195+
.filter(|(_, v)| v.masked)
196+
.map(|(_, v)| v.value.as_str())
197+
.collect::<Vec<_>>();
198+
let masker = Masker::new(&masked_variables, GITLAB_MASK);
199+
190200
let job = Job::new(
191201
self.client.clone(),
192202
self.response.clone(),
@@ -198,6 +208,7 @@ impl Run {
198208
job,
199209
self.client.clone(),
200210
self.response.clone(),
211+
masker.clone(),
201212
process,
202213
build_dir,
203214
cancel_token.clone(),
@@ -207,15 +218,6 @@ impl Run {
207218
);
208219
tokio::pin!(join);
209220

210-
let masked_variables = self
211-
.response
212-
.variables
213-
.iter()
214-
.filter(|(_, v)| v.masked)
215-
.map(|(_, v)| v.value.as_str())
216-
.collect::<Vec<_>>();
217-
218-
let masker = Masker::new(&masked_variables, GITLAB_MASK);
219221
let mut cm = masker.mask_chunks();
220222

221223
let result = loop {

gitlab-runner/src/uploader.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::thread;
88
use std::{sync::Arc, task::Poll};
99

1010
use futures::{future::BoxFuture, AsyncWrite, FutureExt};
11+
use masker::{ChunkMasker, Masker};
1112
use reqwest::Body;
1213
use tokio::fs::File as AsyncFile;
1314
use tokio::sync::mpsc::{self, error::SendError};
@@ -71,6 +72,7 @@ fn zip_thread(mut temp: File, mut rx: mpsc::Receiver<UploadRequest>) {
7172
pub struct UploadFile<'a> {
7273
tx: &'a mpsc::Sender<UploadRequest>,
7374
state: UploadFileState<'a>,
75+
masker: Option<ChunkMasker<'a>>,
7476
}
7577

7678
impl<'a> AsyncWrite for UploadFile<'a> {
@@ -84,10 +86,12 @@ impl<'a> AsyncWrite for UploadFile<'a> {
8486
match this.state {
8587
UploadFileState::Idle => {
8688
let (tx, rx) = oneshot::channel();
87-
let send = this
88-
.tx
89-
.send(UploadRequest::WriteData(Vec::from(buf), tx))
90-
.boxed();
89+
let buf = if let Some(masker) = &mut this.masker {
90+
masker.mask_chunk(buf)
91+
} else {
92+
Vec::from(buf)
93+
};
94+
let send = this.tx.send(UploadRequest::WriteData(buf, tx)).boxed();
9195
this.state = UploadFileState::Writing(Some(send), rx)
9296
}
9397
UploadFileState::Writing(ref mut send, ref mut rx) => {
@@ -114,9 +118,33 @@ impl<'a> AsyncWrite for UploadFile<'a> {
114118

115119
fn poll_close(
116120
self: std::pin::Pin<&mut Self>,
117-
_cx: &mut std::task::Context<'_>,
121+
cx: &mut std::task::Context<'_>,
118122
) -> std::task::Poll<std::io::Result<()>> {
119-
Poll::Ready(Ok(()))
123+
let this = self.get_mut();
124+
if let Some(masker) = this.masker.take() {
125+
let (tx, mut rx) = oneshot::channel();
126+
let buf = masker.finish();
127+
let mut send = Some(this.tx.send(UploadRequest::WriteData(buf, tx)).boxed());
128+
129+
loop {
130+
if let Some(mut f) = send {
131+
// Phase 1: Waiting for the send to the writer
132+
// thread to complete.
133+
134+
// TODO error handling
135+
let _r = futures::ready!(f.as_mut().poll(cx));
136+
send = None;
137+
} else {
138+
// Phase 2: Waiting for the writer thread to
139+
// signal write completion.
140+
141+
let _r = futures::ready!(Pin::new(&mut rx).poll(cx));
142+
return Poll::Ready(Ok(()));
143+
}
144+
}
145+
} else {
146+
Poll::Ready(Ok(()))
147+
}
120148
}
121149
}
122150

@@ -125,20 +153,27 @@ pub struct Uploader {
125153
client: Client,
126154
data: Arc<JobResponse>,
127155
tx: mpsc::Sender<UploadRequest>,
156+
masker: Masker,
128157
}
129158

130159
impl Uploader {
131160
pub(crate) fn new(
132161
client: Client,
133162
build_dir: &Path,
134163
data: Arc<JobResponse>,
164+
masker: Masker,
135165
) -> Result<Self, ()> {
136166
let temp = tempfile::tempfile_in(build_dir)
137167
.map_err(|e| warn!("Failed to create artifacts temp file: {:?}", e))?;
138168

139169
let (tx, rx) = mpsc::channel(2);
140170
thread::spawn(move || zip_thread(temp, rx));
141-
Ok(Self { client, data, tx })
171+
Ok(Self {
172+
client,
173+
data,
174+
tx,
175+
masker,
176+
})
142177
}
143178

144179
/// Create a new file to be uploaded
@@ -149,6 +184,20 @@ impl Uploader {
149184
.expect("Failed to create file");
150185
UploadFile {
151186
tx: &self.tx,
187+
masker: None,
188+
state: UploadFileState::Idle,
189+
}
190+
}
191+
192+
/// Create a new file to be uploaded, which will be masked
193+
pub async fn masked_file(&mut self, name: String) -> UploadFile<'_> {
194+
self.tx
195+
.send(UploadRequest::NewFile(name))
196+
.await
197+
.expect("Failed to create file");
198+
UploadFile {
199+
tx: &self.tx,
200+
masker: Some(self.masker.mask_chunks()),
152201
state: UploadFileState::Idle,
153202
}
154203
}

0 commit comments

Comments
 (0)