Skip to content

Commit

Permalink
wip: update cloudflare impl
Browse files Browse the repository at this point in the history
  • Loading branch information
cilki committed Jun 22, 2024
1 parent e06deeb commit d5f702d
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 57 deletions.
8 changes: 3 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ jobs:
GH_REPO: ${{ github.repository }}
run: |
for tag in $(git tag --points-at HEAD); do
tag="outpost-${tag}"
if cp target/${{ matrix.target }}/release/${tag%-*} ${tag%-*}_${{ matrix.target }}; then
gh release upload "${tag}" "${tag%-*}_${{ matrix.target }}"
if cp target/${{ matrix.target }}/release/outpost outpost_${{ matrix.target }}; then
gh release upload "${tag##*-}" "outpost_${{ matrix.target }}"
fi
done
Expand All @@ -85,8 +84,7 @@ jobs:
id: get_tags
run: |
for tag in $(git tag --points-at HEAD); do
tag="outpost-${tag}"
echo "${tag%-*}=${tag##*-}" >>"$GITHUB_OUTPUT"
echo "outpost=${tag##*-}" >>"$GITHUB_OUTPUT"
done
- uses: docker/build-push-action@v5
Expand Down
40 changes: 40 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ version = "0.0.4"
anyhow = "1.0.86"
axum = "0.7.5"
clap = { version = "4.5.4", features = ["string", "derive"] }
futures = "0.3.30"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.117"
serde_yaml = "0.9.27"
tempfile = "3.10.1"
tokio = { version = "1.38.0", features = ["full"] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
uuid = { version = "1.8.0", features = ["v4"] }

[build-dependencies]
anyhow = "1.0.86"
Expand Down
118 changes: 67 additions & 51 deletions src/cloudflare.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use anyhow::Result;
use outpost::PortMapping;
use serde::Serialize;
use std::{
process::{Child, Command},
time::Duration,
};
use tempfile::TempDir;
use tokio::process::{Child, Command};
use tracing::{debug, info, instrument};
use uuid::Uuid;

#[derive(Debug, Serialize)]
pub struct CloudflareConfigIngress {
Expand All @@ -27,58 +23,31 @@ pub struct CloudflareConfig {

#[derive(Debug)]
pub struct CloudflareProxy {
temp: TempDir,
_temp: TempDir,
/// The cloudflared child process which actually handles the routing
pub process: Child,
}

impl Drop for CloudflareProxy {
fn drop(&mut self) {
debug!("Stopping cloudflare tunnel");
futures::executor::block_on(self.process.kill()).unwrap();
}
}

impl CloudflareProxy {
#[instrument(ret)]
pub fn new(service: String, fqdn: String, ports: Vec<PortMapping>) -> Result<Self> {
pub async fn new(service: String, fqdn: String, ports: Vec<PortMapping>) -> Result<Self> {
let temp = TempDir::new()?;

// Generate config
let mut config = CloudflareConfig {
tunnel: Uuid::new_v4().to_string(),
credentials_file: temp
.path()
.join(format!("{}.json", &fqdn))
.to_string_lossy()
.to_string(),
ingress: vec![
// This one is always required to be last
CloudflareConfigIngress {
hostname: None,
service: "http_status:404".into(),
},
],
};

for port in ports {
config.ingress.insert(
0,
CloudflareConfigIngress {
hostname: Some(fqdn.clone()),
service: format!("http://{}:{}", &service, port.local),
},
)
}

debug!(config = ?config, "Generated cloudflared config");

// Write config
let config_path = temp.path().join("config.yml");
std::fs::write(&config_path, serde_yaml::to_string(&config)?)?;

// Make sure the tunnel doesn't already exist
if Command::new("cloudflared")
.arg("tunnel")
.arg("--config")
.arg(config_path.to_string_lossy().to_string())
.arg("delete")
.arg(&service)
.spawn()?
.wait()?
.wait()
.await?
.success()
{
debug!("Deleted existing tunnel successfully");
Expand All @@ -87,31 +56,78 @@ impl CloudflareProxy {
// Create tunnel
assert!(Command::new("cloudflared")
.arg("tunnel")
.arg("--config")
.arg(config_path.to_string_lossy().to_string())
.arg("create")
.arg(&service)
.spawn()?
.wait()?
.wait()
.await?
.success());

// Update DNS record
assert!(Command::new("cloudflared")
.arg("tunnel")
.arg("--config")
.arg(config_path.to_string_lossy().to_string())
.arg("route")
.arg("dns")
.arg("--overwrite-dns")
.arg(&service)
.arg(fqdn)
.arg(&fqdn)
.spawn()?
.wait()?
.wait()
.await?
.success());

// Generate config
let mut config = CloudflareConfig {
tunnel: "".to_string(),
credentials_file: "".to_string(),
ingress: vec![
// This one is always required to be last
CloudflareConfigIngress {
hostname: None,
service: "http_status:404".into(),
},
],
};

// Find tunnel secret file rather than parsing command output
for entry in std::fs::read_dir("/root/.cloudflared")? {
let entry = entry?;

if entry
.file_name()
.to_string_lossy()
.to_owned()
.ends_with(".json")
{
config.tunnel = entry
.path()
.file_stem()
.unwrap()
.to_string_lossy()
.to_string();
config.credentials_file = entry.path().to_string_lossy().to_string();
}
}

for port in ports {
config.ingress.insert(
0,
CloudflareConfigIngress {
hostname: Some(fqdn.clone()),
service: format!("http://{}:{}", &service, port.local),
},
)
}

debug!(config = ?config, "Generated cloudflared config");

// Write config
let config_path = temp.path().join("config.yml");
std::fs::write(&config_path, serde_yaml::to_string(&config)?)?;

info!("Starting cloudflare tunnel");
Ok(Self {
temp,
_temp: temp,
process: Command::new("cloudflared")
// Try to not cede any more control to cloudflare
.arg("--no-autoupdate")
Expand Down
6 changes: 6 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ async fn main() -> Result<ExitCode> {
let ports: Vec<PortMapping> = PortMapping::from_vec(ports)?;
tokio::spawn(async {
crate::cloudflare::CloudflareProxy::new(service, fqdn, ports)
.await
.unwrap()
.process
.wait()
.await
.unwrap();
});
}
}
Expand Down

0 comments on commit d5f702d

Please sign in to comment.