Skip to content

Commit

Permalink
refactor: adjust acme token directory
Browse files Browse the repository at this point in the history
  • Loading branch information
vicanso committed Dec 12, 2024
1 parent 1d41c05 commit 55413ad
Show file tree
Hide file tree
Showing 16 changed files with 128 additions and 49 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Sentry and opentelemetry are optional, they are supported in the full-featured v

[Examples](./examples/README.md)

[Docs](./docs/README.md)

```mermaid
flowchart LR
internet("Internet") -- request --> pingap["Pingap"]
Expand Down
2 changes: 2 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Sentry与opentelemetry为可选的特性,在全功能版本中支持。

[例子](./examples/README.md)

[文档](./docs/README.md)


```mermaid
flowchart LR
Expand Down
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Documents

- [acme chart](./acme_chart.md)
36 changes: 36 additions & 0 deletions docs/acme_chart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# ACME

### Acme Service

```mermaid
graph TD;
start("new acme service") --> validate_certificate{{Load certificate and validate}};
validate_certificate -- not found or expired --> new_acme_order;
new_acme_order --> get_authorizations;
get_authorizations -- load all authorization and save to files --> set_challenges;
set_challenges -- wait for ready --> order_fresh;
order_fresh -- ready --> done;
order_fresh -- not ready --> delay;
delay -- xms*2 --> order_fresh
validate_certificate -- valid --> done("wait for next task");
```


### Http 01 Challenge

```mermaid
graph TD;
start("new http 80") -- wait for http-01 challenge request --> handle_request;
handle_request -- get token from file --> validate_token;
validate_token -- fail(not found) --> done;
validate_token -- success --> done;
```
45 changes: 27 additions & 18 deletions src/acme/lets_encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

use super::{get_token_path, Certificate, Error, Result};
use crate::config::{
get_current_config, load_config, save_config, LoadConfigOptions,
PingapConf, CATEGORY_CERTIFICATE,
get_config_storage, get_current_config, load_config, save_config,
LoadConfigOptions, PingapConf, CATEGORY_CERTIFICATE,
};
use crate::http_extra::HttpResponse;
use crate::proxy::init_certificates;
Expand All @@ -32,7 +32,6 @@ use instant_acme::{
use pingora::proxy::Session;
use std::time::Duration;
use substring::Substring;
use tokio::fs;
use tracing::{error, info};

struct LetsEncryptService {
Expand Down Expand Up @@ -151,19 +150,22 @@ pub async fn handle_lets_encrypt(
if path.starts_with(WELL_KNOWN_PATH_PREFIX) {
// token auth
let token = path.substring(WELL_KNOWN_PATH_PREFIX.len(), path.len());
let Some(storage) = get_config_storage() else {
return Err(util::new_internal_error(
500,
"get config storage fail".to_string(),
));
};

let value = match fs::read_to_string(get_token_path().join(token)).await
{
Ok(value) => Ok(value),
Err(e) => {
let value =
storage.load(&get_token_path(token)).await.map_err(|e| {
error!(
token,
err = e.to_string(),
"let't encrypt http-01 fail"
);
Err(util::new_internal_error(500, e.to_string()))
},
}?;
util::new_internal_error(500, e.to_string())
})?;
info!(token, "let't encrypt http-01 success");
HttpResponse {
status: StatusCode::OK,
Expand Down Expand Up @@ -235,10 +237,11 @@ async fn new_lets_encrypt(
})?;
let mut challenges = Vec::with_capacity(authorizations.len());

let token_path = get_token_path();
fs::create_dir_all(&token_path)
.await
.map_err(|e| Error::Io { source: e })?;
let Some(storage) = get_config_storage() else {
return Err(Error::NotFound {
message: "storage not found".to_string(),
});
};

for authz in &authorizations {
info!(
Expand All @@ -262,11 +265,17 @@ async fn new_lets_encrypt(
let instant_acme::Identifier::Dns(identifier) = &authz.identifier;

let key_auth = order.key_authorization(challenge);

fs::write(token_path.join(&challenge.token), key_auth.as_str())
storage
.save(
&get_token_path(&challenge.token),
key_auth.as_str().as_bytes(),
)
.await
.map_err(|e| Error::Io { source: e })?;
// http://your-domain/.well-known/acme-challenge/<TOKEN>
.map_err(|e| Error::Fail {
category: "save_token".to_string(),
message: e.to_string(),
})?;

let well_known_path =
format!("{WELL_KNOWN_PATH_PREFIX}{}", challenge.token);
info!(well_known_path, "let's encrypt well known path",);
Expand Down
16 changes: 3 additions & 13 deletions src/acme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{config::get_current_config, util};
use crate::util;
use serde::{Deserialize, Serialize};
use snafu::Snafu;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::path::{Path, PathBuf};

#[derive(Debug, Snafu)]
pub enum Error {
Expand All @@ -36,8 +35,6 @@ pub enum Error {
Fail { category: String, message: String },
#[snafu(display("X509 error, category: {category}, {message}"))]
X509 { category: String, message: String },
#[snafu(display("Io error, {source}"))]
Io { source: std::io::Error },
}

type Result<T, E = Error> = std::result::Result<T, E>;
Expand Down Expand Up @@ -131,15 +128,8 @@ impl Certificate {
}
}

pub fn get_token_path() -> PathBuf {
let token_path = util::resolve_path(
&get_current_config()
.basic
.acme_token_directory
.clone()
.unwrap_or("/tmp/pingap-acme".to_string()),
);
Path::new(&token_path).to_path_buf()
pub fn get_token_path(key: &str) -> String {
format!("pingap-acme-tokens/${key}")
}

mod lets_encrypt;
Expand Down
1 change: 0 additions & 1 deletion src/config/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,6 @@ pub struct BasicConf {
pub auto_restart_check_interval: Option<Duration>,
pub cache_directory: Option<String>,
pub cache_max_size: Option<ByteSize>,
pub acme_token_directory: Option<String>,
}

impl BasicConf {
Expand Down
22 changes: 20 additions & 2 deletions src/config/etcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,13 @@ impl ConfigStorage for EtcdStorage {
category: &str,
name: Option<&str>,
) -> Result<()> {
let filepath = self.path.clone();
conf.validate()?;
let (path, toml_value) = if self.separation && name.is_some() {
conf.get_toml(category, name)?
} else {
conf.get_toml(category, None)?
};
let key = format!("{filepath}{path}");
let key = util::path_join(&self.path, &path);
let mut c = self.connect().await?;
if toml_value.is_empty() {
c.delete(key, None)
Expand Down Expand Up @@ -158,6 +157,25 @@ impl ConfigStorage for EtcdStorage {
etcd_watch_stream: Some(stream),
})
}
async fn save(&self, key: &str, data: &[u8]) -> Result<()> {
let key = util::path_join(&self.path, key);
let mut c = self.connect().await?;
c.put(key, data, None)
.await
.map_err(|e| Error::Etcd { source: e })?;
Ok(())
}
async fn load(&self, key: &str) -> Result<Vec<u8>> {
let key = util::path_join(&self.path, key);
let mut c = self.connect().await?;
let arr = c
.get(key, None)
.await
.map_err(|e| Error::Etcd { source: e })?
.take_kvs();
let buf = if arr.is_empty() { b"" } else { arr[0].value() };
Ok(buf.into())
}
}

#[cfg(test)]
Expand Down
26 changes: 25 additions & 1 deletion src/config/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl ConfigStorage for FileStorage {
conf.get_toml(category, None)?
};

let filepath = format!("{filepath}{path}");
let filepath = util::path_join(&filepath, &path);
let target_file = Path::new(&filepath);
if let Some(p) = Path::new(&target_file).parent() {
fs::create_dir_all(p).await.map_err(|e| Error::Io {
Expand All @@ -167,6 +167,30 @@ impl ConfigStorage for FileStorage {

Ok(())
}
async fn save(&self, key: &str, data: &[u8]) -> Result<()> {
let key = util::path_join(&self.path, key);
let path = Path::new(&key);
if let Some(p) = path.parent() {
fs::create_dir_all(p).await.map_err(|e| Error::Io {
source: e,
file: key.to_string(),
})?;
}
fs::write(path, data).await.map_err(|e| Error::Io {
source: e,
file: key.to_string(),
})?;
Ok(())
}
async fn load(&self, key: &str) -> Result<Vec<u8>> {
let key = util::path_join(&self.path, key);
let path = Path::new(&key);
let buf = fs::read(path).await.map_err(|e| Error::Io {
source: e,
file: key.to_string(),
})?;
Ok(buf)
}
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ pub trait ConfigStorage {
etcd_watch_stream: None,
})
}
async fn save(&self, key: &str, data: &[u8]) -> Result<()>;
async fn load(&self, key: &str) -> Result<Vec<u8>>;
}

static CONFIG_STORAGE: OnceCell<Box<(dyn ConfigStorage + Sync + Send)>> =
Expand Down
1 change: 0 additions & 1 deletion src/proxy/dynamic_certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ fn parse_certificate(
""
};


let hash_key = certificate_config.hash_key();

let tls_chain =
Expand Down
8 changes: 8 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,14 @@ pub fn toml_omit_empty_value(value: &str) -> Result<String, Error> {
})
}

pub fn path_join(value1: &str, value2: &str) -> String {
if value2.starts_with("/") {
format!("{value1}{value2}")
} else {
format!("{value1}/{value2}")
}
}

#[cfg(test)]
mod tests {
use super::{
Expand Down
2 changes: 0 additions & 2 deletions web/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ export default {
cacheMaxSizePlaceholder: "Input max size of cache(e.g. 100mb)",
upgradeSock: "Upgrade Sock For Daemon",
upgradeSockPlaceholder: "Input upgrade unix sock for daemon",
acmeTokenDirectory: "ACME Token Directory",
acmeTokenDirectoryPlaceholder: "Input the directory for save acme token",
user: "User For Daemon",
userPlaceholder: "Input user for daemon",
group: "Group For Daemon",
Expand Down
2 changes: 0 additions & 2 deletions web/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ export default {
cacheMaxSizePlaceholder: "输入最大缓存空间限制(如100mb)",
upgradeSock: "更新配置使用的sock",
upgradeSockPlaceholder: "输入后台服务更新配置使用的sock",
acmeTokenDirectory: "ACME协议认证Token",
acmeTokenDirectoryPlaceholder: "输入保存ACME协议认证Token的目录路径",
user: "启用后台服务的用户",
userPlaceholder: "输入后台服务的用户",
group: "启用后台服务的用户组",
Expand Down
8 changes: 0 additions & 8 deletions web/src/pages/Basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,6 @@ export default function Basic() {
span: 3,
category: ExFormItemCategory.TEXT,
},
{
name: "acme_token_directory",
label: basicI18n("acmeTokenDirectory"),
placeholder: basicI18n("acmeTokenDirectoryPlaceholder"),
defaultValue: basic.acme_token_directory,
span: 3,
category: ExFormItemCategory.TEXT,
},
{
name: "cache_directory",
label: basicI18n("cacheDirectory"),
Expand Down
1 change: 0 additions & 1 deletion web/src/states/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ interface Basic {
auto_restart_check_interval?: string;
cache_max_size?: number;
cache_directory?: string;
acme_token_directory?: string;
sentry?: string;
pyroscope?: string;
webhook?: string;
Expand Down

0 comments on commit 55413ad

Please sign in to comment.