Skip to content

Commit

Permalink
feat: show validity date of certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
vicanso committed Nov 12, 2024
1 parent 4c62dc2 commit 9d40a98
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 14 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ flowchart LR
- Supports regular form configuration to rewrite Path
- Support HTTP 1/2, including h2c
- Support static, dns and docker label service discovery
- Support grpc-web reverse proxy
- Configuration based on TOML format, the configuration method is very simple, and can be saved to files or etcd
- Supports more than 10 Prometheus indicators, pull and push mode
- Opentelemetry supports w3c context trace and jaeger trace
Expand Down
1 change: 1 addition & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ flowchart LR
- 支持正则形式配置重写Path,方便应用按前缀区分转发
- HTTP 1/2 的全链路支持,包括h2c的支持
- 支持静态配置、DNS以及docker label的三种服务发现形式
- 支持grpc-web反向代理
- 基于TOML格式的配置,配置方式非常简洁,可保存至文件或etcd
- 支持10多个Prometheus指标,可以使用pull与push的形式收集相关指标
- Opentelemetry支持w3c context trace与jaeger trace的形式
Expand Down
2 changes: 1 addition & 1 deletion src/acme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub enum Error {

type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, Serialize)]
pub struct CertificateInfo {
pub not_after: i64,
pub not_before: i64,
Expand Down
12 changes: 7 additions & 5 deletions src/acme/validity_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,16 @@ impl ServiceTask for ValidityChecker {
None
}
fn description(&self) -> String {
let certificate_info_list = get_certificate_info_list();
let mut names = vec![];
for (name, _) in get_certificate_info_list().iter() {
if !names.contains(name) {
names.push(name.clone());
}
}

let offset_human: humantime::Duration =
Duration::from_secs(self.time_offset as u64).into();
format!(
"ValidityChecker: {offset_human}, {:?}",
certificate_info_list
)
format!("ValidityChecker: {names:?}, {offset_human}")
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/plugin/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::config::{
};
use crate::http_extra::{HttpResponse, HTTP_HEADER_WWW_AUTHENTICATE};
use crate::limit::TtlLruLimit;
use crate::proxy::get_certificate_info_list;
use crate::state::{
get_process_system_info, get_processing_accepted, get_start_time,
};
Expand All @@ -45,6 +46,7 @@ use pingora::proxy::Session;
use rust_embed::EmbeddedFile;
use rust_embed::RustEmbed;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io::Write;
use std::time::Duration;
use substring::Substring;
Expand Down Expand Up @@ -554,6 +556,14 @@ impl Plugin for AdminServe {
HttpResponse::try_from_json(&AesResp { value }).unwrap_or(
HttpResponse::unknown_error("Json serde fail".into()),
)
} else if path == "/certificates" {
let mut infos = HashMap::new();
for (name, info) in get_certificate_info_list() {
infos.insert(name, info);
}
HttpResponse::try_from_json(&infos).unwrap_or(
HttpResponse::unknown_error("Json serde fail".into()),
)
} else {
let mut file = path.substring(1, path.len());
if file.is_empty() {
Expand Down
20 changes: 14 additions & 6 deletions src/proxy/dynamic_certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ fn parse_certificate(
domains,
certificate: Some((cert, key)),
info: Some(info),
..Default::default()
})
}

Expand All @@ -158,15 +159,16 @@ fn parse_certificates(
let mut errors = vec![];
for (name, certificate) in certificate_configs.iter() {
match parse_certificate(certificate) {
Ok(dynamic_cert) => {
let mut names = dynamic_cert.domains.clone();
Ok(mut dynamic_cert) => {
dynamic_cert.name = Some(name.clone());
let mut domains = dynamic_cert.domains.clone();
let cert = Arc::new(dynamic_cert);
if let Some(value) = &certificate.domains {
names =
domains =
value.split(',').map(|item| item.to_string()).collect();
}
for name in names.iter() {
dynamic_certs.insert(name.to_string(), cert.clone());
for domain in domains.iter() {
dynamic_certs.insert(domain.to_string(), cert.clone());
}
let is_default = certificate.is_default.unwrap_or_default();
if is_default {
Expand Down Expand Up @@ -218,14 +220,20 @@ pub fn get_certificate_info_list() -> Vec<(String, CertificateInfo)> {
let mut infos = vec![];
for (name, cert) in DYNAMIC_CERTIFICATE_MAP.load().iter() {
if let Some(info) = &cert.info {
infos.push((name.to_string(), info.clone()));
let key = if let Some(name) = &cert.name {
name.clone()
} else {
name.clone()
};
infos.push((key, info.clone()));
}
}
infos
}

#[derive(Debug, Clone, Default)]
pub struct DynamicCertificate {
name: Option<String>,
chain_certificate: Option<X509>,
certificate: Option<(X509, PKey<Private>)>,
domains: Vec<String>,
Expand Down
40 changes: 38 additions & 2 deletions web/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import useBasicState from "@/states/basic";
import { useI18n } from "@/i18n";
import { listify } from "radash";
import { Button } from "@/components/ui/button";
import { useAsync } from "react-async-hook";
import React from "react";

interface Summary {
name: string;
Expand All @@ -23,11 +25,41 @@ interface Summary {

export default function Home() {
const homeI18n = useI18n("home");
const [config, initialized] = useConfigState((state) => [
const [config, initialized, getCertificateInfos] = useConfigState((state) => [
state.data,
state.initialized,
state.getCertificateInfos,
]);
const [basicInfo] = useBasicState((state) => [state.data]);
const [validity, setValidity] = React.useState({} as Record<string, string>);
useAsync(async () => {
try {
const infos = await getCertificateInfos();
const formatDate = (value: number) => {
const date = new Date(value * 1000);
let month = `${date.getMonth()}`;
if (month.length === 1) {
month = `0${month}`;
}
let day = `${date.getDate()}`;
if (day.length === 1) {
day = `0${day}`;
}
return `${month}-${day}`;
};
const results = {} as Record<string, string>;
Object.keys(infos).forEach((name) => {
const data = infos[name];
if (data) {
results[name] =
formatDate(data.not_before) + " : " + formatDate(data.not_after);
}
});
setValidity(results);
} catch (err) {
console.error(err);
}
}, []);
if (!initialized) {
return <LoadingPage />;
}
Expand Down Expand Up @@ -120,10 +152,14 @@ export default function Home() {
? `${certificateCount} Certificates`
: `${certificateCount} Certificate`;
listify(config.certificates, (name, value) => {
let date = validity[name] || "";
if (date) {
date = ` (${date})`;
}
certificateSummary.push({
name,
link: `${CERTIFICATES}?name=${name}`,
value: value.domains || "",
value: (value.domains || "") + date,
});
});
}
Expand Down
11 changes: 11 additions & 0 deletions web/src/states/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import request from "@/helpers/request";
import { random } from "@/helpers/util";
import { create } from "zustand";

export interface CertificateInfo {
not_after: number;
not_before: number;
}

export interface Upstream {
addrs: string[];
discovery?: string;
Expand Down Expand Up @@ -158,6 +163,7 @@ interface ConfigState {
) => Promise<void>;
remove: (category: string, name: string) => Promise<void>;
getIncludeOptions: () => string[];
getCertificateInfos: () => Promise<Record<string, CertificateInfo>>;
}

const useConfigState = create<ConfigState>()((set, get) => ({
Expand Down Expand Up @@ -227,6 +233,11 @@ const useConfigState = create<ConfigState>()((set, get) => ({
});
return includes;
},
getCertificateInfos: async () => {
const { data } =
await request.get<Record<string, CertificateInfo>>(`/certificates`);
return data;
},
}));

export default useConfigState;

0 comments on commit 9d40a98

Please sign in to comment.