mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-12 21:56:36 +00:00
Compare commits
4 Commits
1.16.2-s.4
...
1.16.2-s.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90afe5a7ac | ||
|
|
b24de85157 | ||
|
|
eda43dffe1 | ||
|
|
82c9a1eb70 |
@@ -2343,8 +2343,8 @@
|
|||||||
"logRetentionEndOfFollowingYear": "End of following year",
|
"logRetentionEndOfFollowingYear": "End of following year",
|
||||||
"actionLogsDescription": "View a history of actions performed in this organization",
|
"actionLogsDescription": "View a history of actions performed in this organization",
|
||||||
"accessLogsDescription": "View access auth requests for resources in this organization",
|
"accessLogsDescription": "View access auth requests for resources in this organization",
|
||||||
"licenseRequiredToUse": "An <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> license or <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> is required to use this feature.",
|
"licenseRequiredToUse": "An <enterpriseLicenseLink>Enterprise Edition</enterpriseLicenseLink> license or <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink> is required to use this feature. <bookADemoLink>Book a demo or POC trial</bookADemoLink>.",
|
||||||
"ossEnterpriseEditionRequired": "The <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is required to use this feature. This feature is also available in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>.",
|
"ossEnterpriseEditionRequired": "The <enterpriseEditionLink>Enterprise Edition</enterpriseEditionLink> is required to use this feature. This feature is also available in <pangolinCloudLink>Pangolin Cloud</pangolinCloudLink>. <bookADemoLink>Book a demo or POC trial</bookADemoLink>.",
|
||||||
"certResolver": "Certificate Resolver",
|
"certResolver": "Certificate Resolver",
|
||||||
"certResolverDescription": "Select the certificate resolver to use for this resource.",
|
"certResolverDescription": "Select the certificate resolver to use for this resource.",
|
||||||
"selectCertResolver": "Select Certificate Resolver",
|
"selectCertResolver": "Select Certificate Resolver",
|
||||||
|
|||||||
@@ -571,7 +571,7 @@ export async function updateClientSiteDestinations(
|
|||||||
destinations: [
|
destinations: [
|
||||||
{
|
{
|
||||||
destinationIP: site.sites.subnet.split("/")[0],
|
destinationIP: site.sites.subnet.split("/")[0],
|
||||||
destinationPort: site.sites.listenPort || 0
|
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -579,7 +579,7 @@ export async function updateClientSiteDestinations(
|
|||||||
// add to the existing destinations
|
// add to the existing destinations
|
||||||
destinations.destinations.push({
|
destinations.destinations.push({
|
||||||
destinationIP: site.sites.subnet.split("/")[0],
|
destinationIP: site.sites.subnet.split("/")[0],
|
||||||
destinationPort: site.sites.listenPort || 0
|
destinationPort: site.sites.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -218,10 +218,11 @@ export class TraefikConfigManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch if it's been more than 24 hours (for renewals)
|
|
||||||
const dayInMs = 24 * 60 * 60 * 1000;
|
const dayInMs = 24 * 60 * 60 * 1000;
|
||||||
const timeSinceLastFetch =
|
const timeSinceLastFetch =
|
||||||
Date.now() - this.lastCertificateFetch.getTime();
|
Date.now() - this.lastCertificateFetch.getTime();
|
||||||
|
|
||||||
|
// Fetch if it's been more than 24 hours (daily routine check)
|
||||||
if (timeSinceLastFetch > dayInMs) {
|
if (timeSinceLastFetch > dayInMs) {
|
||||||
logger.info("Fetching certificates due to 24-hour renewal check");
|
logger.info("Fetching certificates due to 24-hour renewal check");
|
||||||
return true;
|
return true;
|
||||||
@@ -265,7 +266,7 @@ export class TraefikConfigManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any local certificates are missing or appear to be outdated
|
// Check if any local certificates are missing (needs immediate fetch)
|
||||||
for (const domain of domainsNeedingCerts) {
|
for (const domain of domainsNeedingCerts) {
|
||||||
const localState = this.lastLocalCertificateState.get(domain);
|
const localState = this.lastLocalCertificateState.get(domain);
|
||||||
if (!localState || !localState.exists) {
|
if (!localState || !localState.exists) {
|
||||||
@@ -274,17 +275,55 @@ export class TraefikConfigManager {
|
|||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if certificate is expiring soon (within 30 days)
|
// For expiry checks, throttle to every 6 hours to avoid querying the
|
||||||
if (localState.expiresAt) {
|
// API/DB on every monitor loop. The certificate-service renews certs
|
||||||
const nowInSeconds = Math.floor(Date.now() / 1000);
|
// 45 days before expiry, so checking every 6 hours is plenty frequent
|
||||||
const secondsUntilExpiry = localState.expiresAt - nowInSeconds;
|
// to pick up renewed certs promptly.
|
||||||
const daysUntilExpiry = secondsUntilExpiry / (60 * 60 * 24);
|
const renewalCheckIntervalMs = 6 * 60 * 60 * 1000; // 6 hours
|
||||||
if (daysUntilExpiry < 30) {
|
if (timeSinceLastFetch > renewalCheckIntervalMs) {
|
||||||
logger.info(
|
// Check non-wildcard certs for expiry (within 45 days to match
|
||||||
`Fetching certificates due to upcoming expiry for ${domain} (${Math.round(daysUntilExpiry)} days remaining)`
|
// the server-side renewal window in certificate-service)
|
||||||
);
|
for (const domain of domainsNeedingCerts) {
|
||||||
return true;
|
const localState =
|
||||||
|
this.lastLocalCertificateState.get(domain);
|
||||||
|
if (localState?.expiresAt) {
|
||||||
|
const nowInSeconds = Math.floor(Date.now() / 1000);
|
||||||
|
const secondsUntilExpiry =
|
||||||
|
localState.expiresAt - nowInSeconds;
|
||||||
|
const daysUntilExpiry =
|
||||||
|
secondsUntilExpiry / (60 * 60 * 24);
|
||||||
|
if (daysUntilExpiry < 45) {
|
||||||
|
logger.info(
|
||||||
|
`Fetching certificates due to upcoming expiry for ${domain} (${Math.round(daysUntilExpiry)} days remaining)`
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check wildcard certificates for expiry. These are not
|
||||||
|
// included in domainsNeedingCerts since their subdomains are
|
||||||
|
// filtered out, so we must check them separately.
|
||||||
|
for (const [certDomain, state] of this
|
||||||
|
.lastLocalCertificateState) {
|
||||||
|
if (
|
||||||
|
state.exists &&
|
||||||
|
state.wildcard &&
|
||||||
|
state.expiresAt
|
||||||
|
) {
|
||||||
|
const nowInSeconds = Math.floor(Date.now() / 1000);
|
||||||
|
const secondsUntilExpiry =
|
||||||
|
state.expiresAt - nowInSeconds;
|
||||||
|
const daysUntilExpiry =
|
||||||
|
secondsUntilExpiry / (60 * 60 * 24);
|
||||||
|
if (daysUntilExpiry < 45) {
|
||||||
|
logger.info(
|
||||||
|
`Fetching certificates due to upcoming expiry for wildcard cert ${certDomain} (${Math.round(daysUntilExpiry)} days remaining)`
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,6 +400,32 @@ export class TraefikConfigManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also include wildcard cert base domains that are
|
||||||
|
// expiring or expired so they get re-fetched even though
|
||||||
|
// their subdomains were filtered out above.
|
||||||
|
for (const [certDomain, state] of this
|
||||||
|
.lastLocalCertificateState) {
|
||||||
|
if (
|
||||||
|
state.exists &&
|
||||||
|
state.wildcard &&
|
||||||
|
state.expiresAt
|
||||||
|
) {
|
||||||
|
const nowInSeconds = Math.floor(
|
||||||
|
Date.now() / 1000
|
||||||
|
);
|
||||||
|
const secondsUntilExpiry =
|
||||||
|
state.expiresAt - nowInSeconds;
|
||||||
|
const daysUntilExpiry =
|
||||||
|
secondsUntilExpiry / (60 * 60 * 24);
|
||||||
|
if (daysUntilExpiry < 45) {
|
||||||
|
domainsToFetch.add(certDomain);
|
||||||
|
logger.info(
|
||||||
|
`Including expiring wildcard cert domain ${certDomain} in fetch (${Math.round(daysUntilExpiry)} days remaining)`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (domainsToFetch.size > 0) {
|
if (domainsToFetch.size > 0) {
|
||||||
// Get valid certificates for domains not covered by wildcards
|
// Get valid certificates for domains not covered by wildcards
|
||||||
validCertificates =
|
validCertificates =
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export async function generateRelayMappings(exitNode: ExitNode) {
|
|||||||
// Add site as a destination for this client
|
// Add site as a destination for this client
|
||||||
const destination: PeerDestination = {
|
const destination: PeerDestination = {
|
||||||
destinationIP: site.subnet.split("/")[0],
|
destinationIP: site.subnet.split("/")[0],
|
||||||
destinationPort: site.listenPort
|
destinationPort: site.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if this destination is already in the array to avoid duplicates
|
// Check if this destination is already in the array to avoid duplicates
|
||||||
@@ -165,7 +165,7 @@ export async function generateRelayMappings(exitNode: ExitNode) {
|
|||||||
|
|
||||||
const destination: PeerDestination = {
|
const destination: PeerDestination = {
|
||||||
destinationIP: peer.subnet.split("/")[0],
|
destinationIP: peer.subnet.split("/")[0],
|
||||||
destinationPort: peer.listenPort
|
destinationPort: peer.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for duplicates
|
// Check for duplicates
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export async function updateHolePunch(
|
|||||||
destinations: destinations
|
destinations: destinations
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// logger.error(error); // FIX THIS
|
logger.error(error);
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.INTERNAL_SERVER_ERROR,
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
@@ -262,7 +262,7 @@ export async function updateAndGenerateEndpointDestinations(
|
|||||||
if (site.subnet && site.listenPort) {
|
if (site.subnet && site.listenPort) {
|
||||||
destinations.push({
|
destinations.push({
|
||||||
destinationIP: site.subnet.split("/")[0],
|
destinationIP: site.subnet.split("/")[0],
|
||||||
destinationPort: site.listenPort
|
destinationPort: site.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,11 +104,11 @@ export const handleGetConfigMessage: MessageHandler = async (context) => {
|
|||||||
const payload = {
|
const payload = {
|
||||||
oldDestination: {
|
oldDestination: {
|
||||||
destinationIP: existingSite.subnet?.split("/")[0],
|
destinationIP: existingSite.subnet?.split("/")[0],
|
||||||
destinationPort: existingSite.listenPort
|
destinationPort: existingSite.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
},
|
},
|
||||||
newDestination: {
|
newDestination: {
|
||||||
destinationIP: site.subnet?.split("/")[0],
|
destinationIP: site.subnet?.split("/")[0],
|
||||||
destinationPort: site.listenPort
|
destinationPort: site.listenPort || 1 // this satisfies gerbil for now but should be reevaluated
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const docsLinkClassName =
|
|||||||
const PANGOLIN_CLOUD_SIGNUP_URL = "https://app.pangolin.net/auth/signup/";
|
const PANGOLIN_CLOUD_SIGNUP_URL = "https://app.pangolin.net/auth/signup/";
|
||||||
const ENTERPRISE_DOCS_URL =
|
const ENTERPRISE_DOCS_URL =
|
||||||
"https://docs.pangolin.net/self-host/enterprise-edition";
|
"https://docs.pangolin.net/self-host/enterprise-edition";
|
||||||
|
const BOOK_A_DEMO_URL = "https://click.fossorial.io/ep922";
|
||||||
|
|
||||||
function getTierLinkRenderer(billingHref: string) {
|
function getTierLinkRenderer(billingHref: string) {
|
||||||
return function tierLinkRenderer(chunks: React.ReactNode) {
|
return function tierLinkRenderer(chunks: React.ReactNode) {
|
||||||
@@ -78,6 +79,22 @@ function getPangolinCloudLinkRenderer() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBookADemoLinkRenderer() {
|
||||||
|
return function bookADemoLinkRenderer(chunks: React.ReactNode) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={BOOK_A_DEMO_URL}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={docsLinkClassName}
|
||||||
|
>
|
||||||
|
{chunks}
|
||||||
|
<ExternalLink className="size-3.5 shrink-0" />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getDocsLinkRenderer(href: string) {
|
function getDocsLinkRenderer(href: string) {
|
||||||
return function docsLinkRenderer(chunks: React.ReactNode) {
|
return function docsLinkRenderer(chunks: React.ReactNode) {
|
||||||
return (
|
return (
|
||||||
@@ -116,6 +133,7 @@ export function PaidFeaturesAlert({ tiers }: Props) {
|
|||||||
const tierLinkRenderer = getTierLinkRenderer(billingHref);
|
const tierLinkRenderer = getTierLinkRenderer(billingHref);
|
||||||
const pangolinCloudLinkRenderer = getPangolinCloudLinkRenderer();
|
const pangolinCloudLinkRenderer = getPangolinCloudLinkRenderer();
|
||||||
const enterpriseDocsLinkRenderer = getDocsLinkRenderer(ENTERPRISE_DOCS_URL);
|
const enterpriseDocsLinkRenderer = getDocsLinkRenderer(ENTERPRISE_DOCS_URL);
|
||||||
|
const bookADemoLinkRenderer = getBookADemoLinkRenderer();
|
||||||
|
|
||||||
if (env.flags.disableEnterpriseFeatures) {
|
if (env.flags.disableEnterpriseFeatures) {
|
||||||
return null;
|
return null;
|
||||||
@@ -157,7 +175,8 @@ export function PaidFeaturesAlert({ tiers }: Props) {
|
|||||||
{t.rich("licenseRequiredToUse", {
|
{t.rich("licenseRequiredToUse", {
|
||||||
enterpriseLicenseLink:
|
enterpriseLicenseLink:
|
||||||
enterpriseDocsLinkRenderer,
|
enterpriseDocsLinkRenderer,
|
||||||
pangolinCloudLink: pangolinCloudLinkRenderer
|
pangolinCloudLink: pangolinCloudLinkRenderer,
|
||||||
|
bookADemoLink: bookADemoLinkRenderer
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -174,7 +193,8 @@ export function PaidFeaturesAlert({ tiers }: Props) {
|
|||||||
{t.rich("ossEnterpriseEditionRequired", {
|
{t.rich("ossEnterpriseEditionRequired", {
|
||||||
enterpriseEditionLink:
|
enterpriseEditionLink:
|
||||||
enterpriseDocsLinkRenderer,
|
enterpriseDocsLinkRenderer,
|
||||||
pangolinCloudLink: pangolinCloudLinkRenderer
|
pangolinCloudLink: pangolinCloudLinkRenderer,
|
||||||
|
bookADemoLink: bookADemoLinkRenderer
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user