mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: updated gateway to quic
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
FROM node:20-alpine
|
FROM node:20-slim
|
||||||
|
|
||||||
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
||||||
|
|
||||||
@@ -7,32 +7,32 @@ ARG SOFTHSM2_VERSION=2.5.0
|
|||||||
ENV SOFTHSM2_VERSION=${SOFTHSM2_VERSION} \
|
ENV SOFTHSM2_VERSION=${SOFTHSM2_VERSION} \
|
||||||
SOFTHSM2_SOURCES=/tmp/softhsm2
|
SOFTHSM2_SOURCES=/tmp/softhsm2
|
||||||
|
|
||||||
# install build dependencies including python3 (required for pkcs11js and partially TDS driver)
|
# Install build dependencies including python3 (required for pkcs11js and partially TDS driver)
|
||||||
RUN apk --update add \
|
RUN apt-get update && apt-get install -y \
|
||||||
alpine-sdk \
|
build-essential \
|
||||||
autoconf \
|
autoconf \
|
||||||
automake \
|
automake \
|
||||||
git \
|
git \
|
||||||
libtool \
|
libtool \
|
||||||
openssl-dev \
|
libssl-dev \
|
||||||
python3 \
|
python3 \
|
||||||
make \
|
make \
|
||||||
g++ \
|
g++ \
|
||||||
openssh
|
openssh-client \
|
||||||
|
curl \
|
||||||
|
pkg-config
|
||||||
|
|
||||||
# install dependencies for TDS driver (required for SAP ASE dynamic secrets)
|
# Install dependencies for TDS driver (required for SAP ASE dynamic secrets)
|
||||||
RUN apk add --no-cache \
|
RUN apt-get install -y \
|
||||||
unixodbc \
|
unixodbc \
|
||||||
freetds \
|
|
||||||
unixodbc-dev \
|
unixodbc-dev \
|
||||||
libc-dev \
|
freetds-dev \
|
||||||
freetds-dev
|
freetds-bin \
|
||||||
|
tdsodbc
|
||||||
|
|
||||||
|
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nFileUsage = 1\n" > /etc/odbcinst.ini
|
||||||
|
|
||||||
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/libtdsodbc.so\nSetup = /usr/lib/libtdsodbc.so\nFileUsage = 1\n" > /etc/odbcinst.ini
|
# Build and install SoftHSM2
|
||||||
|
|
||||||
# build and install SoftHSM2
|
|
||||||
|
|
||||||
RUN git clone https://github.com/opendnssec/SoftHSMv2.git ${SOFTHSM2_SOURCES}
|
RUN git clone https://github.com/opendnssec/SoftHSMv2.git ${SOFTHSM2_SOURCES}
|
||||||
WORKDIR ${SOFTHSM2_SOURCES}
|
WORKDIR ${SOFTHSM2_SOURCES}
|
||||||
|
|
||||||
@@ -45,16 +45,18 @@ RUN git checkout ${SOFTHSM2_VERSION} -b ${SOFTHSM2_VERSION} \
|
|||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
RUN rm -fr ${SOFTHSM2_SOURCES}
|
RUN rm -fr ${SOFTHSM2_SOURCES}
|
||||||
|
|
||||||
# install pkcs11-tool
|
# Install pkcs11-tool
|
||||||
RUN apk --update add opensc
|
RUN apt-get install -y opensc
|
||||||
|
|
||||||
RUN softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
RUN mkdir -p /etc/softhsm2/tokens && \
|
||||||
|
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
||||||
|
|
||||||
# ? App setup
|
# ? App setup
|
||||||
|
|
||||||
RUN apk add --no-cache bash curl && curl -1sLf \
|
# Install Infisical CLI
|
||||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \
|
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
|
||||||
&& apk add infisical=0.8.1 && apk add --no-cache git
|
apt-get update && \
|
||||||
|
apt-get install -y infisical=0.8.1
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,7 @@
|
|||||||
"@fastify/swagger": "^8.14.0",
|
"@fastify/swagger": "^8.14.0",
|
||||||
"@fastify/swagger-ui": "^2.1.0",
|
"@fastify/swagger-ui": "^2.1.0",
|
||||||
"@google-cloud/kms": "^4.5.0",
|
"@google-cloud/kms": "^4.5.0",
|
||||||
|
"@infisical/quic": "^1.0.8",
|
||||||
"@node-saml/passport-saml": "^4.0.4",
|
"@node-saml/passport-saml": "^4.0.4",
|
||||||
"@octokit/auth-app": "^7.1.1",
|
"@octokit/auth-app": "^7.1.1",
|
||||||
"@octokit/plugin-retry": "^5.0.5",
|
"@octokit/plugin-retry": "^5.0.5",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -474,7 +473,7 @@ export const gatewayServiceFactory = ({
|
|||||||
relayHost,
|
relayHost,
|
||||||
relayPort: Number(relayPort),
|
relayPort: Number(relayPort),
|
||||||
tlsOptions: {
|
tlsOptions: {
|
||||||
key: privateKey,
|
key: privateKey.toString(),
|
||||||
ca: `${gatewayCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
ca: `${gatewayCaCert.toString("pem")}\n${rootCaCert.toString("pem")}`.trim(),
|
||||||
cert: clientCert.toString("pem")
|
cert: clientCert.toString("pem")
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
|
import crypto from "node:crypto";
|
||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
import tls from "node:tls";
|
|
||||||
|
import { QUICClient } from "@infisical/quic";
|
||||||
|
import { CryptoError } from "@infisical/quic/dist/native";
|
||||||
|
|
||||||
import { BadRequestError } from "../errors";
|
import { BadRequestError } from "../errors";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
@@ -8,34 +11,71 @@ import { logger } from "../logger";
|
|||||||
const DEFAULT_MAX_RETRIES = 3;
|
const DEFAULT_MAX_RETRIES = 3;
|
||||||
const DEFAULT_RETRY_DELAY = 1000; // 1 second
|
const DEFAULT_RETRY_DELAY = 1000; // 1 second
|
||||||
|
|
||||||
const createTLSConnection = (relayHost: string, relayPort: number, tlsOptions: tls.TlsOptions = {}) => {
|
const parseSubjectDetails = (data: string) => {
|
||||||
return new Promise<tls.TLSSocket>((resolve, reject) => {
|
const values: Record<string, string> = {};
|
||||||
// @ts-expect-error this is resolved in next connect
|
data.split("\n").forEach((el) => {
|
||||||
const socket = new tls.TLSSocket(null, {
|
const [key, value] = el.split("=");
|
||||||
rejectUnauthorized: true,
|
values[key.trim()] = value.trim();
|
||||||
...tlsOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
const cleanup = () => {
|
|
||||||
socket.removeAllListeners();
|
|
||||||
socket.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.once("error", (err) => {
|
|
||||||
cleanup();
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.connect(relayPort, relayHost, () => {
|
|
||||||
resolve(socket);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TTlsOption = { ca: string; cert: string; key: string };
|
||||||
|
|
||||||
|
const createQuicConnection = async (
|
||||||
|
relayHost: string,
|
||||||
|
relayPort: number,
|
||||||
|
tlsOptions: TTlsOption,
|
||||||
|
identityId: string,
|
||||||
|
orgId: string
|
||||||
|
) => {
|
||||||
|
const client = await QUICClient.createQUICClient({
|
||||||
|
host: relayHost,
|
||||||
|
port: relayPort,
|
||||||
|
config: {
|
||||||
|
ca: tlsOptions.ca,
|
||||||
|
cert: tlsOptions.cert,
|
||||||
|
key: tlsOptions.key,
|
||||||
|
applicationProtos: ["infisical-gateway"],
|
||||||
|
verifyPeer: true,
|
||||||
|
verifyCallback: async (certs) => {
|
||||||
|
if (!certs || certs.length === 0) return CryptoError.CertificateRequired;
|
||||||
|
const serverCertificate = new crypto.X509Certificate(Buffer.from(certs[0]));
|
||||||
|
const caCertificate = new crypto.X509Certificate(tlsOptions.ca);
|
||||||
|
const isValidServerCertificate = serverCertificate.checkIssued(caCertificate);
|
||||||
|
if (!isValidServerCertificate) return CryptoError.BadCertificate;
|
||||||
|
|
||||||
|
const subjectDetails = parseSubjectDetails(serverCertificate.subject);
|
||||||
|
if (subjectDetails.OU !== "Gateway" || subjectDetails.CN !== identityId || subjectDetails.O !== orgId) {
|
||||||
|
return CryptoError.CertificateUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new Date() > new Date(serverCertificate.validTo) || new Date() < new Date(serverCertificate.validFrom)) {
|
||||||
|
return CryptoError.CertificateExpired;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatedRelayHost =
|
||||||
|
process.env.NODE_ENV === "development" ? relayHost.replace("host.docker.internal", "127.0.0.1") : relayHost;
|
||||||
|
if (!serverCertificate.checkIP(formatedRelayHost)) return CryptoError.BadCertificate;
|
||||||
|
},
|
||||||
|
maxIdleTimeout: 90000,
|
||||||
|
keepAliveIntervalTime: 30000
|
||||||
|
},
|
||||||
|
crypto: {
|
||||||
|
ops: {
|
||||||
|
randomBytes: async (data) => {
|
||||||
|
crypto.getRandomValues(new Uint8Array(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return client;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TPingGatewayAndVerifyDTO = {
|
type TPingGatewayAndVerifyDTO = {
|
||||||
relayHost: string;
|
relayHost: string;
|
||||||
relayPort: number;
|
relayPort: number;
|
||||||
tlsOptions: tls.TlsOptions;
|
tlsOptions: TTlsOption;
|
||||||
maxRetries?: number;
|
maxRetries?: number;
|
||||||
identityId: string;
|
identityId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
@@ -44,56 +84,44 @@ type TPingGatewayAndVerifyDTO = {
|
|||||||
export const pingGatewayAndVerify = async ({
|
export const pingGatewayAndVerify = async ({
|
||||||
relayHost,
|
relayHost,
|
||||||
relayPort,
|
relayPort,
|
||||||
tlsOptions = {},
|
tlsOptions,
|
||||||
maxRetries = DEFAULT_MAX_RETRIES,
|
maxRetries = DEFAULT_MAX_RETRIES,
|
||||||
identityId,
|
identityId,
|
||||||
orgId
|
orgId
|
||||||
}: TPingGatewayAndVerifyDTO) => {
|
}: TPingGatewayAndVerifyDTO) => {
|
||||||
let lastError: Error | null = null;
|
let lastError: Error | null = null;
|
||||||
|
const quic = await createQuicConnection(relayHost, relayPort, tlsOptions, identityId, orgId).catch((err) => {
|
||||||
|
throw new BadRequestError({
|
||||||
|
error: err as Error
|
||||||
|
});
|
||||||
|
});
|
||||||
for (let attempt = 1; attempt <= maxRetries; attempt += 1) {
|
for (let attempt = 1; attempt <= maxRetries; attempt += 1) {
|
||||||
try {
|
try {
|
||||||
const socket = await createTLSConnection(relayHost, relayPort, tlsOptions);
|
const stream = quic.connection.newStream("bidi");
|
||||||
socket.setTimeout(2000);
|
const pingWriter = stream.writable.getWriter();
|
||||||
|
await pingWriter.write(Buffer.from("PING\n"));
|
||||||
|
pingWriter.releaseLock();
|
||||||
|
|
||||||
const pingResult = await new Promise((resolve, reject) => {
|
// Read PONG response
|
||||||
socket.once("timeout", () => {
|
const reader = stream.readable.getReader();
|
||||||
socket.destroy();
|
const { value, done } = await reader.read();
|
||||||
reject(new Error("Timeout"));
|
|
||||||
|
if (done) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Gateway closed before receiving PONG"
|
||||||
});
|
});
|
||||||
socket.once("close", () => {
|
}
|
||||||
socket.destroy();
|
|
||||||
|
const response = Buffer.from(value).toString();
|
||||||
|
|
||||||
|
if (response !== "PONG\n" && response !== "PONG") {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Failed to Ping. Unexpected response: ${response}`
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
socket.once("end", () => {
|
reader.releaseLock();
|
||||||
socket.destroy();
|
return;
|
||||||
});
|
|
||||||
socket.once("error", (err) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.write(Buffer.from("PING\n"), () => {
|
|
||||||
socket.once("data", (data) => {
|
|
||||||
const response = (data as string).toString();
|
|
||||||
const certificate = socket.getPeerCertificate();
|
|
||||||
|
|
||||||
if (certificate.subject.CN !== identityId || certificate.subject.O !== orgId) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `Invalid gateway. Certificate not found for ${identityId} in organization ${orgId}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response === "PONG") {
|
|
||||||
resolve(true);
|
|
||||||
} else {
|
|
||||||
reject(new Error(`Unexpected response: ${response}`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.end();
|
|
||||||
return pingResult;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
lastError = err as Error;
|
lastError = err as Error;
|
||||||
|
|
||||||
@@ -102,6 +130,8 @@ export const pingGatewayAndVerify = async ({
|
|||||||
setTimeout(resolve, DEFAULT_RETRY_DELAY);
|
setTimeout(resolve, DEFAULT_RETRY_DELAY);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
await quic.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,76 +144,125 @@ export const pingGatewayAndVerify = async ({
|
|||||||
interface TProxyServer {
|
interface TProxyServer {
|
||||||
server: net.Server;
|
server: net.Server;
|
||||||
port: number;
|
port: number;
|
||||||
cleanup: () => void;
|
cleanup: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupProxyServer = ({
|
const setupProxyServer = async ({
|
||||||
targetPort,
|
targetPort,
|
||||||
targetHost,
|
targetHost,
|
||||||
tlsOptions = {},
|
tlsOptions,
|
||||||
relayHost,
|
relayHost,
|
||||||
relayPort
|
relayPort,
|
||||||
|
identityId,
|
||||||
|
orgId
|
||||||
}: {
|
}: {
|
||||||
targetHost: string;
|
targetHost: string;
|
||||||
targetPort: number;
|
targetPort: number;
|
||||||
relayPort: number;
|
relayPort: number;
|
||||||
relayHost: string;
|
relayHost: string;
|
||||||
tlsOptions: tls.TlsOptions;
|
tlsOptions: TTlsOption;
|
||||||
|
identityId: string;
|
||||||
|
orgId: string;
|
||||||
}): Promise<TProxyServer> => {
|
}): Promise<TProxyServer> => {
|
||||||
|
const quic = await createQuicConnection(relayHost, relayPort, tlsOptions, identityId, orgId).catch((err) => {
|
||||||
|
throw new BadRequestError({
|
||||||
|
error: err as Error
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const server = net.createServer();
|
const server = net.createServer();
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
server.on("connection", async (clientSocket) => {
|
server.on("connection", async (clientConn) => {
|
||||||
try {
|
try {
|
||||||
const targetSocket = await createTLSConnection(relayHost, relayPort, tlsOptions);
|
clientConn.setKeepAlive(true, 30000); // 30 seconds
|
||||||
|
clientConn.setNoDelay(true);
|
||||||
|
|
||||||
targetSocket.write(Buffer.from(`FORWARD-TCP ${targetHost}:${targetPort}\n`), () => {
|
const stream = quic.connection.newStream("bidi");
|
||||||
clientSocket.on("data", (data) => {
|
// Send FORWARD-TCP command
|
||||||
const flushed = targetSocket.write(data);
|
const forwardWriter = stream.writable.getWriter();
|
||||||
if (!flushed) {
|
await forwardWriter.write(Buffer.from(`FORWARD-TCP ${targetHost}:${targetPort}\n`));
|
||||||
clientSocket.pause();
|
forwardWriter.releaseLock();
|
||||||
targetSocket.once("drain", () => {
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||||
clientSocket.resume();
|
// Set up bidirectional copy
|
||||||
});
|
const setupCopy = async () => {
|
||||||
}
|
// Client to QUIC
|
||||||
});
|
// eslint-disable-next-line
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const writer = stream.writable.getWriter();
|
||||||
|
|
||||||
targetSocket.on("data", (data) => {
|
// Create a handler for client data
|
||||||
const flushed = clientSocket.write(data as string);
|
clientConn.on("data", async (chunk) => {
|
||||||
if (!flushed) {
|
await writer.write(chunk);
|
||||||
targetSocket.pause();
|
|
||||||
clientSocket.once("drain", () => {
|
|
||||||
targetSocket.resume();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle client connection close
|
||||||
|
clientConn.on("end", async () => {
|
||||||
|
await writer.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
clientConn.on("error", async (err) => {
|
||||||
|
await writer.abort(err);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
clientConn.destroy();
|
||||||
}
|
}
|
||||||
});
|
})();
|
||||||
|
|
||||||
|
// QUIC to Client
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
const reader = stream.readable.getReader();
|
||||||
|
|
||||||
|
let reading = true;
|
||||||
|
while (reading) {
|
||||||
|
const { value, done } = await reader.read();
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
reading = false;
|
||||||
|
clientConn.end(); // Close client connection when QUIC stream ends
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to TCP client
|
||||||
|
const canContinue = clientConn.write(Buffer.from(value));
|
||||||
|
|
||||||
|
// Handle backpressure
|
||||||
|
if (!canContinue) {
|
||||||
|
await new Promise((res) => {
|
||||||
|
clientConn.once("drain", res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
clientConn.destroy();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
await setupCopy();
|
||||||
|
//
|
||||||
|
// Handle connection closure
|
||||||
|
clientConn.on("close", async () => {
|
||||||
|
await stream.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = async () => {
|
||||||
clientSocket?.unpipe();
|
clientConn?.destroy();
|
||||||
clientSocket?.end();
|
await stream.destroy();
|
||||||
targetSocket?.unpipe();
|
|
||||||
targetSocket?.end();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
clientSocket.on("error", (err) => {
|
clientConn.on("error", (err) => {
|
||||||
logger.error(err, "Client socket error");
|
logger.error(err, "Client socket error");
|
||||||
cleanup();
|
void cleanup();
|
||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
targetSocket.on("error", (err) => {
|
clientConn.on("end", cleanup);
|
||||||
logger.error(err, "Target socket error");
|
|
||||||
cleanup();
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
clientSocket.on("end", cleanup);
|
|
||||||
targetSocket.on("end", cleanup);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err, "Failed to establish target connection:");
|
logger.error(err, "Failed to establish target connection:");
|
||||||
clientSocket.end();
|
clientConn.end();
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -192,6 +271,12 @@ const setupProxyServer = ({
|
|||||||
reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.on("close", async () => {
|
||||||
|
await quic?.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* eslint-enable */
|
||||||
|
|
||||||
server.listen(0, () => {
|
server.listen(0, () => {
|
||||||
const address = server.address();
|
const address = server.address();
|
||||||
if (!address || typeof address === "string") {
|
if (!address || typeof address === "string") {
|
||||||
@@ -204,8 +289,9 @@ const setupProxyServer = ({
|
|||||||
resolve({
|
resolve({
|
||||||
server,
|
server,
|
||||||
port: address.port,
|
port: address.port,
|
||||||
cleanup: () => {
|
cleanup: async () => {
|
||||||
server.close();
|
server.close();
|
||||||
|
await quic?.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -217,8 +303,7 @@ interface ProxyOptions {
|
|||||||
targetPort: number;
|
targetPort: number;
|
||||||
relayHost: string;
|
relayHost: string;
|
||||||
relayPort: number;
|
relayPort: number;
|
||||||
tlsOptions?: tls.TlsOptions;
|
tlsOptions: TTlsOption;
|
||||||
maxRetries?: number;
|
|
||||||
identityId: string;
|
identityId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
}
|
}
|
||||||
@@ -227,30 +312,19 @@ export const withGatewayProxy = async (
|
|||||||
callback: (port: number) => Promise<void>,
|
callback: (port: number) => Promise<void>,
|
||||||
options: ProxyOptions
|
options: ProxyOptions
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const {
|
const { relayHost, relayPort, targetHost, targetPort, tlsOptions, identityId, orgId } = options;
|
||||||
relayHost,
|
|
||||||
relayPort,
|
// Setup the proxy server
|
||||||
|
const { port, cleanup } = await setupProxyServer({
|
||||||
targetHost,
|
targetHost,
|
||||||
targetPort,
|
targetPort,
|
||||||
tlsOptions = {},
|
|
||||||
maxRetries = DEFAULT_MAX_RETRIES,
|
|
||||||
identityId,
|
|
||||||
orgId
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
// First, try to ping the gateway
|
|
||||||
await pingGatewayAndVerify({
|
|
||||||
relayHost,
|
|
||||||
relayPort,
|
relayPort,
|
||||||
|
relayHost,
|
||||||
tlsOptions,
|
tlsOptions,
|
||||||
maxRetries,
|
|
||||||
identityId,
|
identityId,
|
||||||
orgId
|
orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup the proxy server
|
|
||||||
const { port, cleanup } = await setupProxyServer({ targetHost, targetPort, relayPort, relayHost, tlsOptions });
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Execute the callback with the allocated port
|
// Execute the callback with the allocated port
|
||||||
await callback(port);
|
await callback(port);
|
||||||
@@ -259,6 +333,6 @@ export const withGatewayProxy = async (
|
|||||||
throw new BadRequestError({ message: (err as Error)?.message });
|
throw new BadRequestError({ message: (err as Error)?.message });
|
||||||
} finally {
|
} finally {
|
||||||
// Ensure cleanup happens regardless of success or failure
|
// Ensure cleanup happens regardless of success or failure
|
||||||
cleanup();
|
await cleanup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
20
cli/go.mod
20
cli/go.mod
@@ -1,6 +1,8 @@
|
|||||||
module github.com/Infisical/infisical-merge
|
module github.com/Infisical/infisical-merge
|
||||||
|
|
||||||
go 1.21
|
go 1.23.0
|
||||||
|
|
||||||
|
toolchain go1.23.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
|
github.com/bradleyjkemp/cupaloy/v2 v2.8.0
|
||||||
@@ -21,12 +23,13 @@ require (
|
|||||||
github.com/pion/logging v0.2.3
|
github.com/pion/logging v0.2.3
|
||||||
github.com/pion/turn/v4 v4.0.0
|
github.com/pion/turn/v4 v4.0.0
|
||||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a
|
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a
|
||||||
|
github.com/quic-go/quic-go v0.50.0
|
||||||
github.com/rs/cors v1.11.0
|
github.com/rs/cors v1.11.0
|
||||||
github.com/rs/zerolog v1.26.1
|
github.com/rs/zerolog v1.26.1
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/spf13/viper v1.8.1
|
github.com/spf13/viper v1.8.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
golang.org/x/crypto v0.33.0
|
golang.org/x/crypto v0.35.0
|
||||||
golang.org/x/term v0.29.0
|
golang.org/x/term v0.29.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
@@ -58,13 +61,15 @@ require (
|
|||||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/errors v0.20.2 // indirect
|
github.com/go-openapi/errors v0.20.2 // indirect
|
||||||
github.com/go-openapi/strfmt v0.21.3 // indirect
|
github.com/go-openapi/strfmt v0.21.3 // indirect
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 // indirect
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
github.com/google/s2a-go v0.1.7 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
||||||
@@ -82,6 +87,7 @@ require (
|
|||||||
github.com/muesli/mango-pflag v0.1.0 // indirect
|
github.com/muesli/mango-pflag v0.1.0 // indirect
|
||||||
github.com/muesli/termenv v0.15.2 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
|
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.3 // indirect
|
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||||
github.com/pion/dtls/v3 v3.0.4 // indirect
|
github.com/pion/dtls/v3 v3.0.4 // indirect
|
||||||
github.com/pion/randutil v0.1.0 // indirect
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
@@ -103,17 +109,21 @@ require (
|
|||||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 // indirect
|
||||||
|
golang.org/x/mod v0.23.0 // indirect
|
||||||
|
golang.org/x/net v0.35.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
golang.org/x/sync v0.11.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
golang.org/x/time v0.6.0 // indirect
|
golang.org/x/time v0.6.0 // indirect
|
||||||
|
golang.org/x/tools v0.30.0 // indirect
|
||||||
google.golang.org/api v0.188.0 // indirect
|
google.golang.org/api v0.188.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect
|
||||||
google.golang.org/grpc v1.64.1 // indirect
|
google.golang.org/grpc v1.64.1 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.36.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
34
cli/go.sum
34
cli/go.sum
@@ -144,8 +144,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9
|
|||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
|
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
|
||||||
@@ -154,6 +154,8 @@ github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtK
|
|||||||
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
|
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
|
||||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
@@ -222,6 +224,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
|
|||||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 h1:+J3r2e8+RsmN3vKfo75g0YSY61ms37qzPglu4p0sGro=
|
||||||
|
github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||||
@@ -342,6 +346,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
|||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||||
|
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||||
|
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
@@ -369,6 +377,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
|
|||||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a h1:Ey0XWvrg6u6hyIn1Kd/jCCmL+bMv9El81tvuGBbxZGg=
|
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a h1:Ey0XWvrg6u6hyIn1Kd/jCCmL+bMv9El81tvuGBbxZGg=
|
||||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
|
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
||||||
|
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
@@ -461,6 +471,8 @@ go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8p
|
|||||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@@ -472,8 +484,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -484,6 +496,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c=
|
||||||
|
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@@ -509,6 +523,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||||
|
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -547,8 +563,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -697,6 +713,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
|
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||||
|
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -811,8 +829,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
|||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -109,6 +109,33 @@ var gatewayCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gatewayRelayCmd = &cobra.Command{
|
||||||
|
Example: `infisical gateway relay`,
|
||||||
|
Short: "Used to run infisical gateway relay",
|
||||||
|
Use: "relay",
|
||||||
|
DisableFlagsInUseLine: true,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
relayConfigFilePath, err := cmd.Flags().GetString("config")
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "Unable to parse flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if relayConfigFilePath == "" {
|
||||||
|
util.HandleError(fmt.Errorf("Missing config file"))
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayRelay, err := gateway.NewGatewayRelay(relayConfigFilePath)
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "Failed to initialize gateway")
|
||||||
|
}
|
||||||
|
err = gatewayRelay.Run()
|
||||||
|
if err != nil {
|
||||||
|
util.HandleError(err, "Failed to start gateway")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
gatewayCmd.SetHelpFunc(func(command *cobra.Command, strings []string) {
|
gatewayCmd.SetHelpFunc(func(command *cobra.Command, strings []string) {
|
||||||
command.Flags().MarkHidden("domain")
|
command.Flags().MarkHidden("domain")
|
||||||
@@ -116,5 +143,9 @@ func init() {
|
|||||||
})
|
})
|
||||||
gatewayCmd.Flags().String("token", "", "Connect with Infisical using machine identity access token")
|
gatewayCmd.Flags().String("token", "", "Connect with Infisical using machine identity access token")
|
||||||
|
|
||||||
|
gatewayRelayCmd.Flags().String("config", "", "Relay config yaml file path")
|
||||||
|
|
||||||
|
gatewayCmd.AddCommand(gatewayRelayCmd)
|
||||||
|
|
||||||
rootCmd.AddCommand(gatewayCmd)
|
rootCmd.AddCommand(gatewayCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,49 @@ package gateway
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleConnection(conn net.Conn) {
|
func handleConnection(ctx context.Context, quicConn quic.Connection) {
|
||||||
defer conn.Close()
|
log.Info().Msgf("New connection from: %s", quicConn.RemoteAddr().String())
|
||||||
log.Info().Msgf("New connection from: %s", conn.RemoteAddr().String())
|
// Use WaitGroup to track all streams
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for {
|
||||||
|
// Accept the first stream, which we'll use for commands
|
||||||
|
stream, err := quicConn.AcceptStream(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to accept QUIC stream: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func(stream quic.Stream) {
|
||||||
|
defer wg.Done()
|
||||||
|
defer stream.Close()
|
||||||
|
|
||||||
|
handleStream(stream, quicConn)
|
||||||
|
}(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
log.Printf("All streams closed for connection: %s", quicConn.RemoteAddr().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStream(stream quic.Stream, quicConn quic.Connection) {
|
||||||
|
streamID := stream.StreamID()
|
||||||
|
log.Printf("New stream %d from: %s", streamID, quicConn.RemoteAddr().String())
|
||||||
|
|
||||||
// Use buffered reader for better handling of fragmented data
|
// Use buffered reader for better handling of fragmented data
|
||||||
reader := bufio.NewReader(conn)
|
reader := bufio.NewReader(stream)
|
||||||
|
defer stream.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
msg, err := reader.ReadBytes('\n')
|
msg, err := reader.ReadBytes('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -32,6 +61,7 @@ func handleConnection(conn net.Conn) {
|
|||||||
|
|
||||||
switch string(cmd) {
|
switch string(cmd) {
|
||||||
case "FORWARD-TCP":
|
case "FORWARD-TCP":
|
||||||
|
log.Info().Msg("Starting secure connector proxy...")
|
||||||
proxyAddress := string(bytes.Split(args, []byte(" "))[0])
|
proxyAddress := string(bytes.Split(args, []byte(" "))[0])
|
||||||
destTarget, err := net.Dial("tcp", proxyAddress)
|
destTarget, err := net.Dial("tcp", proxyAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -56,10 +86,10 @@ func handleConnection(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyData(conn, destTarget)
|
CopyDataFromQuicToTcp(stream, destTarget)
|
||||||
return
|
return
|
||||||
case "PING":
|
case "PING":
|
||||||
if _, err := conn.Write([]byte("PONG")); err != nil {
|
if _, err := stream.Write([]byte("PONG\n")); err != nil {
|
||||||
log.Error().Msgf("Error writing PONG response: %v", err)
|
log.Error().Msgf("Error writing PONG response: %v", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -74,34 +104,38 @@ type CloseWrite interface {
|
|||||||
CloseWrite() error
|
CloseWrite() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func CopyData(src, dst net.Conn) {
|
func CopyDataFromQuicToTcp(quicStream quic.Stream, tcpConn net.Conn) {
|
||||||
|
// Create a WaitGroup to wait for both copy operations
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
||||||
copyAndClose := func(dst, src net.Conn, done chan<- bool) {
|
// Start copying from QUIC stream to TCP
|
||||||
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
_, err := io.Copy(dst, src)
|
if _, err := io.Copy(tcpConn, quicStream); err != nil {
|
||||||
if err != nil && !errors.Is(err, io.EOF) {
|
log.Error().Msgf("Error copying quic->postgres: %v", err)
|
||||||
log.Error().Msgf("Copy error: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal we're done writing
|
if e, ok := tcpConn.(CloseWrite); ok {
|
||||||
done <- true
|
log.Debug().Msg("Closing TCP write end")
|
||||||
|
e.CloseWrite()
|
||||||
// Half close the connection if possible
|
} else {
|
||||||
if c, ok := dst.(CloseWrite); ok {
|
log.Debug().Msg("TCP connection does not support CloseWrite")
|
||||||
c.CloseWrite()
|
|
||||||
}
|
}
|
||||||
}
|
}()
|
||||||
|
|
||||||
done1 := make(chan bool, 1)
|
// Start copying from TCP to QUIC stream
|
||||||
done2 := make(chan bool, 1)
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
go copyAndClose(dst, src, done1)
|
if _, err := io.Copy(quicStream, tcpConn); err != nil {
|
||||||
go copyAndClose(src, dst, done2)
|
log.Debug().Msgf("Error copying postgres->quic: %v", err)
|
||||||
|
}
|
||||||
|
// Close the write side of the QUIC stream
|
||||||
|
if err := quicStream.Close(); err != nil && !strings.Contains(err.Error(), "close called for canceled stream") {
|
||||||
|
log.Error().Msgf("Error closing QUIC stream write: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Wait for both copies to complete
|
// Wait for both copies to complete
|
||||||
<-done1
|
|
||||||
<-done2
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/pion/logging"
|
"github.com/pion/logging"
|
||||||
"github.com/pion/turn/v4"
|
"github.com/pion/turn/v4"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GatewayConfig struct {
|
type GatewayConfig struct {
|
||||||
@@ -56,13 +58,12 @@ func (g *Gateway) ConnectWithRelay() error {
|
|||||||
if relayPort == "5349" {
|
if relayPort == "5349" {
|
||||||
log.Info().Msgf("Provided relay port %s. Using TLS", relayPort)
|
log.Info().Msgf("Provided relay port %s. Using TLS", relayPort)
|
||||||
conn, err = tls.Dial("tcp", relayDetails.TurnServerAddress, &tls.Config{
|
conn, err = tls.Dial("tcp", relayDetails.TurnServerAddress, &tls.Config{
|
||||||
InsecureSkipVerify: false,
|
ServerName: relayAddress,
|
||||||
ServerName: relayAddress,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
log.Info().Msgf("Provided relay port %s. Using non TLS connection.", relayPort)
|
log.Info().Msgf("Provided relay port %s. Using non TLS connection.", relayPort)
|
||||||
peerAddr, err := net.ResolveTCPAddr("tcp", relayDetails.TurnServerAddress)
|
peerAddr, errPeer := net.ResolveTCPAddr("tcp", relayDetails.TurnServerAddress)
|
||||||
if err != nil {
|
if errPeer != nil {
|
||||||
return fmt.Errorf("Failed to parse turn server address: %w", err)
|
return fmt.Errorf("Failed to parse turn server address: %w", err)
|
||||||
}
|
}
|
||||||
conn, err = net.DialTCP("tcp", nil, peerAddr)
|
conn, err = net.DialTCP("tcp", nil, peerAddr)
|
||||||
@@ -116,20 +117,20 @@ func (g *Gateway) Listen(ctx context.Context) error {
|
|||||||
// Allocate a relay socket on the TURN server. On success, it
|
// Allocate a relay socket on the TURN server. On success, it
|
||||||
// will return a net.PacketConn which represents the remote
|
// will return a net.PacketConn which represents the remote
|
||||||
// socket.
|
// socket.
|
||||||
relayNonTlsConn, err := g.client.AllocateTCP()
|
relayUdpConnection, err := g.client.Allocate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to allocate relay connection: %w", err)
|
return fmt.Errorf("Failed to allocate relay connection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Msg(relayNonTlsConn.Addr().String())
|
log.Info().Msg(relayUdpConnection.LocalAddr().String())
|
||||||
defer func() {
|
defer func() {
|
||||||
if closeErr := relayNonTlsConn.Close(); closeErr != nil {
|
if closeErr := relayUdpConnection.Close(); closeErr != nil {
|
||||||
log.Error().Msgf("Failed to close connection: %s", closeErr)
|
log.Error().Msgf("Failed to close connection: %s", closeErr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
gatewayCert, err := api.CallExchangeRelayCertV1(g.httpClient, api.ExchangeRelayCertRequestV1{
|
gatewayCert, err := api.CallExchangeRelayCertV1(g.httpClient, api.ExchangeRelayCertRequestV1{
|
||||||
RelayAddress: relayNonTlsConn.Addr().String(),
|
RelayAddress: relayUdpConnection.LocalAddr().String(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -144,45 +145,58 @@ func (g *Gateway) Listen(ctx context.Context) error {
|
|||||||
|
|
||||||
if g.config.InfisicalStaticIp != "" {
|
if g.config.InfisicalStaticIp != "" {
|
||||||
log.Info().Msgf("Found static ip from Infisical: %s. Creating permission IP lifecycle", g.config.InfisicalStaticIp)
|
log.Info().Msgf("Found static ip from Infisical: %s. Creating permission IP lifecycle", g.config.InfisicalStaticIp)
|
||||||
peerAddr, err := net.ResolveTCPAddr("tcp", g.config.InfisicalStaticIp)
|
peerAddr, err := net.ResolveUDPAddr("udp", g.config.InfisicalStaticIp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to parse infisical static ip: %w", err)
|
return fmt.Errorf("Failed to parse infisical static ip: %w", err)
|
||||||
}
|
}
|
||||||
g.registerPermissionLifecycle(func() error {
|
err = g.client.CreatePermission(peerAddr)
|
||||||
err := relayNonTlsConn.CreatePermissions(peerAddr)
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Failed to set permission: %w", err)
|
||||||
}, shutdownCh)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, err := tls.X509KeyPair([]byte(gatewayCert.Certificate), []byte(gatewayCert.PrivateKey))
|
cert, err := tls.X509KeyPair([]byte(gatewayCert.Certificate), []byte(gatewayCert.PrivateKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse cert: %s", err)
|
return fmt.Errorf("failed to parse cert: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
caCertPool := x509.NewCertPool()
|
caCertPool := x509.NewCertPool()
|
||||||
caCertPool.AppendCertsFromPEM([]byte(gatewayCert.CertificateChain))
|
caCertPool.AppendCertsFromPEM([]byte(gatewayCert.CertificateChain))
|
||||||
|
|
||||||
relayConn := tls.NewListener(relayNonTlsConn, &tls.Config{
|
// Setup QUIC server
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
Certificates: []tls.Certificate{cert},
|
Certificates: []tls.Certificate{cert},
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
ClientCAs: caCertPool,
|
ClientCAs: caCertPool,
|
||||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||||
})
|
NextProtos: []string{"infisical-gateway"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup QUIC listener on the relayConn
|
||||||
|
quicConfig := &quic.Config{
|
||||||
|
EnableDatagrams: true,
|
||||||
|
MaxIdleTimeout: 30 * time.Second,
|
||||||
|
KeepAlivePeriod: 15 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
quicListener, err := quic.Listen(relayUdpConnection, tlsConfig, quicConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to listen for QUIC: %w", err)
|
||||||
|
}
|
||||||
|
defer quicListener.Close()
|
||||||
|
|
||||||
|
log.Printf("Listener started on %s", quicListener.Addr())
|
||||||
|
|
||||||
errCh := make(chan error, 1)
|
errCh := make(chan error, 1)
|
||||||
log.Info().Msg("Gateway started successfully")
|
log.Info().Msg("Gateway started successfully")
|
||||||
g.registerHeartBeat(errCh, shutdownCh)
|
g.registerHeartBeat(errCh, shutdownCh)
|
||||||
g.registerRelayIsActive(relayNonTlsConn.Addr().String(), errCh, shutdownCh)
|
g.registerRelayIsActive(relayUdpConnection.LocalAddr().String(), tlsConfig, quicConfig, errCh, shutdownCh)
|
||||||
|
|
||||||
// Create a WaitGroup to track active connections
|
// Create a WaitGroup to track active connections
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
if relayDeadlineConn, ok := relayConn.(*net.TCPListener); ok {
|
|
||||||
relayDeadlineConn.SetDeadline(time.Now().Add(1 * time.Second))
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
@@ -190,67 +204,42 @@ func (g *Gateway) Listen(ctx context.Context) error {
|
|||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// Accept new relay connection
|
// Accept new relay connection
|
||||||
conn, err := relayConn.Accept()
|
quicConn, err := quicListener.Accept(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Check if it's a timeout error (which we expect due to our deadline)
|
log.Printf("Failed to accept QUIC connection: %v", err)
|
||||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(err.Error(), "data contains incomplete STUN or TURN frame") {
|
|
||||||
log.Error().Msgf("Failed to accept connection: %v", err)
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConn, ok := conn.(*tls.Conn)
|
tlsState := quicConn.ConnectionState().TLS
|
||||||
if !ok {
|
if len(tlsState.PeerCertificates) > 0 {
|
||||||
log.Error().Msg("Failed to convert to TLS connection")
|
organizationUnit := tlsState.PeerCertificates[0].Subject.OrganizationalUnit
|
||||||
conn.Close()
|
commonName := tlsState.PeerCertificates[0].Subject.CommonName
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a deadline for the handshake to prevent hanging
|
|
||||||
tlsConn.SetDeadline(time.Now().Add(10 * time.Second))
|
|
||||||
err = tlsConn.Handshake()
|
|
||||||
// Clear the deadline after handshake
|
|
||||||
tlsConn.SetDeadline(time.Time{})
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Msgf("TLS handshake failed: %v", err)
|
|
||||||
conn.Close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get connection state which contains certificate information
|
|
||||||
state := tlsConn.ConnectionState()
|
|
||||||
if len(state.PeerCertificates) > 0 {
|
|
||||||
organizationUnit := state.PeerCertificates[0].Subject.OrganizationalUnit
|
|
||||||
commonName := state.PeerCertificates[0].Subject.CommonName
|
|
||||||
if organizationUnit[0] != "gateway-client" || commonName != "cloud" {
|
if organizationUnit[0] != "gateway-client" || commonName != "cloud" {
|
||||||
log.Error().Msgf("Client certificate verification failed. Received %s, %s", organizationUnit, commonName)
|
errMsg := fmt.Sprintf("Client certificate verification failed. Received %s, %s", organizationUnit, commonName)
|
||||||
conn.Close()
|
log.Error().Msg(errMsg)
|
||||||
|
quicConn.CloseWithError(1, errMsg)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the connection in a goroutine
|
// Handle the connection in a goroutine
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(c net.Conn) {
|
go func(c quic.Connection) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer c.Close()
|
defer c.CloseWithError(0, "connection closed")
|
||||||
|
|
||||||
// Monitor parent context to close this connection when needed
|
// Monitor parent context to close this connection when needed
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
c.Close() // Force close connection when context is canceled
|
c.CloseWithError(0, "connection closed") // Force close connection when context is canceled
|
||||||
case <-shutdownCh:
|
case <-shutdownCh:
|
||||||
c.Close() // Force close connection when accepting loop is done
|
c.CloseWithError(0, "connection closed") // Force close connection when accepting loop is done
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
handleConnection(c)
|
handleConnection(ctx, c)
|
||||||
}(conn)
|
}(quicConn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -282,7 +271,7 @@ func (g *Gateway) Listen(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) registerHeartBeat(errCh chan error, done chan bool) {
|
func (g *Gateway) registerHeartBeat(errCh chan error, done chan bool) {
|
||||||
ticker := time.NewTicker(1 * time.Hour)
|
ticker := time.NewTicker(30 * time.Minute)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
@@ -306,26 +295,7 @@ func (g *Gateway) registerHeartBeat(errCh chan error, done chan bool) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gateway) registerPermissionLifecycle(permissionFn func() error, done chan bool) {
|
func (g *Gateway) registerRelayIsActive(serverAddr string, tlsConf *tls.Config, quicConf *quic.Config, errCh chan error, done chan bool) {
|
||||||
ticker := time.NewTicker(3 * time.Minute)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// wait for 5 mins
|
|
||||||
permissionFn()
|
|
||||||
log.Printf("Created permission for incoming connections")
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
ticker.Stop()
|
|
||||||
return
|
|
||||||
case <-ticker.C:
|
|
||||||
permissionFn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Gateway) registerRelayIsActive(serverAddr string, errCh chan error, done chan bool) {
|
|
||||||
ticker := time.NewTicker(10 * time.Second)
|
ticker := time.NewTicker(10 * time.Second)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -336,14 +306,19 @@ func (g *Gateway) registerRelayIsActive(serverAddr string, errCh chan error, don
|
|||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
conn, err := net.Dial("tcp", serverAddr)
|
func() {
|
||||||
if err != nil {
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // 3s handshake timeout
|
||||||
errCh <- err
|
defer cancel()
|
||||||
return
|
conn, err := quic.DialAddr(ctx, serverAddr, tlsConf, quicConf)
|
||||||
}
|
if conn != nil {
|
||||||
if conn != nil {
|
conn.CloseWithError(0, "connection closed")
|
||||||
conn.Close()
|
}
|
||||||
}
|
// this error means quic connection is alive
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "tls: failed to verify certificate") {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
186
cli/packages/gateway/relay.go
Normal file
186
cli/packages/gateway/relay.go
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
package gateway
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/pion/logging"
|
||||||
|
"github.com/pion/turn/v4"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMissingTlsCert = errors.New("Missing TLS files")
|
||||||
|
)
|
||||||
|
|
||||||
|
type GatewayRelay struct {
|
||||||
|
Config *GatewayRelayConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type GatewayRelayConfig struct {
|
||||||
|
PublicIP string `json:"public_ip"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
Realm string `json:"realm"`
|
||||||
|
AuthSecret string `json:"auth_secret"`
|
||||||
|
RelayMinPort uint16 `json:"relay_min_port"`
|
||||||
|
RelayMaxPort uint16 `json:"relay_max_port"`
|
||||||
|
TlsCertPath string `json:"tls_cert_path"`
|
||||||
|
TlsPrivateKeyPath string `json:"tls_private_key_path"`
|
||||||
|
|
||||||
|
tls tls.Certificate
|
||||||
|
isTlsEnabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGatewayRelay(configFilePath string) (*GatewayRelay, error) {
|
||||||
|
cfgFile, err := os.ReadFile(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var cfg GatewayRelayConfig
|
||||||
|
if err := yaml.Unmarshal(cfgFile, &cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.PublicIP == "" {
|
||||||
|
return nil, fmt.Errorf("Missing public ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.AuthSecret == "" {
|
||||||
|
return nil, fmt.Errorf("Missing auth secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Realm == "" {
|
||||||
|
cfg.Realm = "infisical.org"
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.RelayMinPort == 0 {
|
||||||
|
cfg.RelayMinPort = 49152
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.RelayMaxPort == 0 {
|
||||||
|
cfg.RelayMaxPort = 65535
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Port == 0 {
|
||||||
|
cfg.Port = 3478
|
||||||
|
} else if cfg.Port == 5349 {
|
||||||
|
if cfg.TlsCertPath == "" || cfg.TlsPrivateKeyPath == "" {
|
||||||
|
return nil, errMissingTlsCert
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsCertFile, err := os.ReadFile(cfg.TlsCertPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsPrivateKeyFile, err := os.ReadFile(cfg.TlsPrivateKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := tls.LoadX509KeyPair(string(tlsCertFile), string(tlsPrivateKeyFile))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg.tls = cert
|
||||||
|
cfg.isTlsEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return &GatewayRelay{
|
||||||
|
Config: &cfg,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GatewayRelay) Run() error {
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+strconv.Itoa(g.Config.Port))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse server address: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLongTermAuthHandler takes a pion.LeveledLogger. This allows you to intercept messages
|
||||||
|
// and process them yourself.
|
||||||
|
logger := logging.NewDefaultLeveledLoggerForScope("lt-creds", logging.LogLevelTrace, os.Stdout)
|
||||||
|
|
||||||
|
// Create `numThreads` UDP listeners to pass into pion/turn
|
||||||
|
// pion/turn itself doesn't allocate any UDP sockets, but lets the user pass them in
|
||||||
|
// this allows us to add logging, storage or modify inbound/outbound traffic
|
||||||
|
// UDP listeners share the same local address:port with setting SO_REUSEPORT and the kernel
|
||||||
|
// will load-balance received packets per the IP 5-tuple
|
||||||
|
listenerConfig := &net.ListenConfig{
|
||||||
|
Control: func(network, address string, conn syscall.RawConn) error { // nolint: revive
|
||||||
|
var operr error
|
||||||
|
if err = conn.Control(func(fd uintptr) {
|
||||||
|
operr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return operr
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
publicIP := g.Config.PublicIP
|
||||||
|
relayAddressGenerator := &turn.RelayAddressGeneratorPortRange{
|
||||||
|
RelayAddress: net.ParseIP(publicIP), // Claim that we are listening on IP passed by user
|
||||||
|
Address: "0.0.0.0", // But actually be listening on every interface
|
||||||
|
MinPort: g.Config.RelayMinPort,
|
||||||
|
MaxPort: g.Config.RelayMaxPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
threadNum := runtime.NumCPU()
|
||||||
|
listenerConfigs := make([]turn.ListenerConfig, threadNum)
|
||||||
|
for i := 0; i < threadNum; i++ {
|
||||||
|
conn, listErr := listenerConfig.Listen(context.Background(), addr.Network(), addr.String())
|
||||||
|
if listErr != nil {
|
||||||
|
return fmt.Errorf("Failed to allocate TCP listener at %s:%s %s", addr.Network(), addr.String(), listErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
listenerConfigs[i] = turn.ListenerConfig{
|
||||||
|
RelayAddressGenerator: relayAddressGenerator,
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.Config.isTlsEnabled {
|
||||||
|
listenerConfigs[i].Listener = tls.NewListener(conn, &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{g.Config.tls},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
listenerConfigs[i].Listener = conn
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Server %d listening on %s\n", i, conn.Addr().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
loggerF := logging.NewDefaultLoggerFactory()
|
||||||
|
loggerF.DefaultLogLevel = logging.LogLevelDebug
|
||||||
|
|
||||||
|
server, err := turn.NewServer(turn.ServerConfig{
|
||||||
|
Realm: g.Config.Realm,
|
||||||
|
AuthHandler: turn.LongTermTURNRESTAuthHandler(g.Config.AuthSecret, logger),
|
||||||
|
// PacketConnConfigs is a list of UDP Listeners and the configuration around them
|
||||||
|
ListenerConfigs: listenerConfigs,
|
||||||
|
LoggerFactory: loggerF,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to start server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block until user sends SIGINT or SIGTERM
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-sigs
|
||||||
|
|
||||||
|
if err = server.Close(); err != nil {
|
||||||
|
return fmt.Errorf("Failed to close server: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user