warpgate: 0.26.1 -> 0.28.4 (#555116)

This commit is contained in:
Peder Bergebakken Sundt
2026-09-13 12:49:46 +00:00
committed by GitHub
6 changed files with 235 additions and 107 deletions

View File

@@ -45,6 +45,17 @@ in
default = null;
};
databaseEncryptionKeysFile = mkOption {
description = ''
Path to file containing encryption key(s) to encrypt target credentials stored in database.
Should be a env-like file: `WARPGATE_ENCRYPTION_KEY=$(openssl rand -base64 32)`.
If you are rotating key, move the old key to `WARPGATE_ENCRYPTION_KEY_OLD`.
See [Encrypting credentials at rest](https://warpgate.null.page/encryption/).
'';
type = nullOr str;
default = null;
};
settings = mkOption {
description = "Warpgate configuration.";
type = submodule {
@@ -120,18 +131,6 @@ in
]
'';
};
recordings = {
enable = mkOption {
description = "Whether to enable session recording.";
default = true;
type = bool;
};
path = mkOption {
description = "Path to store session recordings.";
default = "/var/lib/warpgate/recordings";
type = str;
};
};
external_host = mkOption {
description = ''
Configure the domain name of this Warpgate instance.
@@ -160,6 +159,11 @@ in
default = "[::]:2222";
type = str;
};
proxy_protocol = mkOption {
description = "Accept HAProxy PROXY protocol v1/v2 headers from upstream reverse proxy.";
default = false;
type = bool;
};
external_host = mkOption {
description = "The SSH listener is reachable via this domain name externally.";
default = null;
@@ -201,6 +205,11 @@ in
default = "[::]:8888";
type = str;
};
proxy_protocol = mkOption {
description = "Accept HAProxy PROXY protocol v1/v2 headers from upstream reverse proxy.";
default = false;
type = bool;
};
external_host = mkOption {
description = "The HTTP listener is reachable via this domain name externally.";
default = null;
@@ -270,6 +279,88 @@ in
type = str;
};
};
rdp = {
enable = mkOption {
description = "Whether to enable RDP listener.";
default = false;
type = bool;
};
listen = mkOption {
description = "Listen endpoint of RDP listener.";
default = "[::]:3389";
type = str;
};
proxy_protocol = mkOption {
description = "Accept HAProxy PROXY protocol v1/v2 headers from the listener's peer.";
default = false;
type = bool;
};
external_host = mkOption {
description = "The RDP listener is reachable via this domain name externally.";
default = null;
type = nullOr str;
};
external_port = mkOption {
description = "The RDP listener is reachable via this port externally.";
default = null;
type = nullOr str;
};
certificate = mkOption {
description = "Path to RDP listener certificate.";
default = "/var/lib/warpgate/tls.certificate.pem";
type = str;
};
key = mkOption {
description = "Path to RDP listener private key.";
default = "/var/lib/warpgate/tls.key.pem";
type = str;
};
};
vnc = {
enable = mkOption {
description = "Whether to enable VNC listener.";
default = false;
type = bool;
};
listen = mkOption {
description = "Listen endpoint of VNC listener.";
default = "[::]:5900";
type = str;
};
proxy_protocol = mkOption {
description = "Accept HAProxy PROXY protocol v1/v2 headers from the listener's peer.";
default = false;
type = bool;
};
external_host = mkOption {
description = "The VNC listener is reachable via this domain name externally.";
default = null;
type = nullOr str;
};
external_port = mkOption {
description = "The VNC listener is reachable via this port externally.";
default = null;
type = nullOr str;
};
certificate = mkOption {
description = "Path to VNC listener certificate.";
default = "/var/lib/warpgate/tls.certificate.pem";
type = str;
};
key = mkOption {
description = "Path to VNC listener private key.";
default = "/var/lib/warpgate/tls.key.pem";
type = str;
};
enable_ard_auth = mkOption {
description = ''
Enable Apple-DH (Apple Remote Desktop / type 30) auth, which is to ensure compatibility with Apple clients.
However [connections from macOS built-in VNC client with ARD auth is not supported](https://github.com/warp-tech/warpgate/blob/47e676969a0b1e0b8456f9a5f1474d6c58648c4f/warpgate-protocol-vnc/src/server/rfb.rs#L8-L10).
'';
default = false;
type = bool;
};
};
mysql = {
enable = mkOption {
description = "Whether to enable MySQL listener.";
@@ -281,6 +372,11 @@ in
default = "[::]:33306";
type = str;
};
proxy_protocol = mkOption {
description = "Accept HAProxy PROXY protocol v1/v2 headers from upstream reverse proxy.";
default = false;
type = bool;
};
external_host = mkOption {
description = "The MySQL listener is reachable via this domain name externally.";
default = null;
@@ -301,6 +397,14 @@ in
default = "/var/lib/warpgate/tls.key.pem";
type = str;
};
advertised_version = mkOption {
description = ''
The server version advertised to clients during the handshake.
Warpgate can't auto-match the target's version since the target is only known after the handshake, but Warpgate's clients use it to pick a protocol dialect.
'';
default = "8.0.3-Warpgate";
type = str;
};
};
postgres = {
enable = mkOption {
@@ -313,6 +417,11 @@ in
default = "[::]:55432";
type = str;
};
proxy_protocol = mkOption {
description = "Accept HAProxy PROXY protocol v1/v2 headers from upstream reverse proxy.";
default = false;
type = bool;
};
external_host = mkOption {
description = "The PostgreSQL listener is reachable via this domain name externally.";
default = null;
@@ -345,6 +454,11 @@ in
default = "[::]:8443";
type = str;
};
proxy_protocol = mkOption {
description = "Accept HAProxy PROXY protocol v1/v2 headers from upstream reverse proxy.";
default = false;
type = bool;
};
external_host = mkOption {
description = "The Kubernetes listener is reachable via this domain name externally.";
default = null;
@@ -420,36 +534,45 @@ in
any
map
head
optional
reverseList
;
inherit (lib.strings) splitString toIntBase10;
inherit (lib.strings)
optionalString
splitString
toIntBase10
;
preStartScript = pkgs.writers.writeBash "warpgate-init" ''
CFGFILE=/var/lib/warpgate/config.yaml
renderedYamlConfig = yaml.generate "warpgate-config" cfg.settings;
startupScript = pkgs.writeShellScript "warpgate-run" ''
CFGFILE=$STATE_DIRECTORY/config.yaml
if [ ! -O $CFGFILE ] || [ ! -s $CFGFILE ]; then
INITPWD=$(tr -dc 'A-Za-z0-9!?%=' </dev/urandom 2>/dev/null | head -c 16)
${lib.getExe cfg.package} \
--config $CFGFILE unattended-setup \
--data-path /var/lib/warpgate \
--data-path $STATE_DIRECTORY \
--http-port 8888 \
--admin-password $INITPWD
fi
${
if cfg.databaseUrlFile != null then
''
sed -e '/^database_url: null/d' ${yaml.generate "warpgate-config" cfg.settings} > $CFGFILE
cat /run/credentials/warpgate.service/databaseUrl >> $CFGFILE
''
else
"cp --no-preserve=ownership ${yaml.generate "warpgate-config" cfg.settings} $CFGFILE"
}
cp --no-preserve=ownership ${renderedYamlConfig} $CFGFILE
${optionalString (cfg.databaseUrlFile != null) ''
sed -e '/^database_url: null/d' ${renderedYamlConfig} > $CFGFILE
cat $CREDENTIALS_DIRECTORY/databaseUrl >> $CFGFILE
''}
${optionalString (cfg.databaseEncryptionKeysFile != null) ''
set -a
source $CREDENTIALS_DIRECTORY/dbEncryptionKeys
set +a
''}
${lib.getExe cfg.package} --config $CFGFILE run
'';
bindOnPrivilegedPorts = any (x: toIntBase10 x < 1025) (
map (x: head (reverseList (splitString ":" x))) (
[ cfg.settings.http.listen ]
++ lib.optional cfg.settings.ssh.enable cfg.settings.ssh.listen
++ lib.optional cfg.settings.mysql.enable cfg.settings.mysql.listen
++ lib.optional cfg.settings.postgres.enable cfg.settings.postgres.listen
++ optional cfg.settings.ssh.enable cfg.settings.ssh.listen
++ optional cfg.settings.mysql.enable cfg.settings.mysql.listen
++ optional cfg.settings.postgres.enable cfg.settings.postgres.listen
)
);
in
@@ -467,6 +590,10 @@ in
assertion = !(lib.hasAttr "config_provider" cfg.settings);
message = "`services.warpgate.settings.config_provider` is a legacy option that has been removed since 0.14.0. Please do not set this option.";
}
{
assertion = !(lib.hasAttr "recordings" cfg.settings);
message = "`services.warpgate.settings.recordings` has been deprecated by S3 recording storage support in 0.27.0. Please remove this section from your config and set it from admin UI.";
}
];
environment.systemPackages = [ cfg.package ];
@@ -474,14 +601,16 @@ in
systemd.services.warpgate = {
description = "Warpgate smart bastion";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
startLimitBurst = 5;
serviceConfig = {
LoadCredential = "${
if cfg.databaseUrlFile != null then "databaseUrl:${cfg.databaseUrlFile}" else ""
}";
ExecStartPre = preStartScript;
ExecStart = "${lib.getExe cfg.package} --config /var/lib/warpgate/config.yaml run";
LoadCredential =
optional (cfg.databaseUrlFile != null) "databaseUrl:${cfg.databaseUrlFile}"
++ optional (
cfg.databaseEncryptionKeysFile != null
) "dbEncryptionKeys:${cfg.databaseEncryptionKeysFile}";
ExecStart = startupScript;
DynamicUser = true;
RestartSec = 3;
Restart = "on-failure";

View File

@@ -1,3 +1,4 @@
{ pkgs, ... }:
{
name = "warpgate";
@@ -9,14 +10,29 @@
};
machine2 = {
environment.etc."warpgate-db-url".text = "database: sqlite:/var/lib/warpgate/db/";
environment.etc."warpgate-db-url".text =
"database_url: postgresql://warpgate:warpgate@localhost:5432/warpgate";
environment.etc."warpgate-db-enc".text =
"WARPGATE_ENCRYPTION_KEY=QVJBTkRPTTMyQ0hBUkFDVEVSU0VOQ1JZUFRJT05LRVk=";
services.warpgate = {
enable = true;
databaseUrlFile = "/etc/warpgate-db-url";
databaseEncryptionKeysFile = "/etc/warpgate-db-enc";
settings = {
database_url = null;
};
};
services.postgresql = {
enable = true;
initialScript = pkgs.writeText "psql-init" ''
CREATE ROLE warpgate WITH LOGIN PASSWORD 'warpgate';
CREATE DATABASE warpgate WITH OWNER warpgate;
'';
};
systemd.services.warpgate = {
after = [ "postgresql.target" ];
requires = [ "postgresql.target" ];
};
};
machine3 = {
@@ -24,6 +40,12 @@
enable = true;
settings = {
http.listen = "[::]:443";
ssh.enable = true;
rdp.enable = true;
vnc.enable = true;
mysql.enable = true;
postgres.enable = true;
kubernetes.enable = true;
};
};
};
@@ -43,6 +65,12 @@
machine3.wait_for_unit("warpgate.service")
machine3.wait_for_open_port(443)
machine3.wait_for_open_port(2222)
machine3.wait_for_open_port(3389)
machine3.wait_for_open_port(5900)
machine3.wait_for_open_port(33306)
machine3.wait_for_open_port(55432)
machine3.wait_for_open_port(8443)
machine3.succeed("curl -k --fail https://localhost/@warpgate")
machine3.shutdown()
'';

View File

@@ -1,20 +1,12 @@
diff --git a/warpgate-common/src/version.rs b/warpgate-common/src/version.rs
index 0e7985a..62c2b67 100644
index 31104706..ec201419 100644
--- a/warpgate-common/src/version.rs
+++ b/warpgate-common/src/version.rs
@@ -1,14 +1,3 @@
-use git_version::git_version;
-
pub const fn warpgate_version() -> &'static str {
- git_version!(
- args = [
- "--tags",
- "--always",
- "--dirty=-modified",
- "--match",
- "v[0-9]*"
- ],
@@ -9,6 +9,6 @@ pub const fn warpgate_version() -> &'static str {
"--match",
"v[0-9]*"
],
- fallback = "unknown"
- )
+ "v@version@"
+ fallback = "v@version@"
)
}

View File

@@ -7,20 +7,24 @@
openapi-generator-cli,
nixosTests,
nix-update-script,
perl,
withRDPLegacyTLSBackend ? false,
}:
rustPlatform.buildRustPackage (
finalAttrs:
let
warpgate-web = buildNpmPackage {
pname = "${finalAttrs.pname}-web";
webUi = buildNpmPackage {
pname = "warpgate-web";
version = finalAttrs.version;
src = finalAttrs.src;
sourceRoot = "${finalAttrs.src.name}/warpgate-web";
patches = [ ./web-ui-package-json.patch ];
patches = [
./web-ui-package-json.patch
];
npmDepsHash = "sha256-McQI5EmTfrbdcWnYRsoRHjhZphrZVaV/fN9i9MX8XF0=";
npmDepsHash = "sha256-BfmYRfsxdJZuS/c7bGccXXYktsjQ76mjwTFKLvNsGAg=";
nativeBuildInputs = [ openapi-generator-cli ];
@@ -35,45 +39,51 @@ rustPlatform.buildRustPackage (
in
{
pname = "warpgate";
version = "0.26.1";
version = "0.28.4";
src = fetchFromGitHub {
owner = "warp-tech";
repo = "warpgate";
tag = "v${finalAttrs.version}";
hash = "sha256-1Dg7bzhBQNe+u90Tw+kcmVaxV5IK0/t505HZr18qP5I=";
hash = "sha256-BWfkStxPi4LucoADK1YwRZaQwObPtq08eEVK9XG7vfU=";
};
cargoHash = "sha256-A5rRLrqlAZV/3ID8F+wUO8OP3Ocivg7vYrNDiMqRKik=";
cargoHash = "sha256-TVNOCMmL8ICtQImA39jhlfCnghvV90NlRBdSol2MGtY=";
patches = [
(replaceVars ./hardcode-version.patch { inherit (finalAttrs) version; })
./remove-nightly-rustflags.patch
];
env.RUSTFLAGS = "--cfg tokio_unstable";
env = {
# uses nightly feature: gethostname, once_cell_try
RUSTC_BOOTSTRAP = true;
RUSTFLAGS = "--cfg tokio_unstable";
};
nativeBuildInputs = lib.optional withRDPLegacyTLSBackend perl;
buildFeatures = [
"postgres"
"mysql"
"sqlite"
];
]
++ lib.optional withRDPLegacyTLSBackend "rdp-openssl-tls";
preBuild = ''
rm -r .cargo/
ln -rs "${warpgate-web}" warpgate-web/dist
rm -rf .cargo/
ln -rs "${webUi}" warpgate-web/dist
'';
# skip check, project included tests require python stuff and docker
doCheck = false;
passthru = {
inherit warpgate-web;
inherit webUi;
tests = {
inherit (nixosTests) warpgate;
};
updateScript = nix-update-script {
extraArgs = [ "--subpackage=warpgate-web" ];
extraArgs = [ "--subpackage=webUi" ];
};
};

View File

@@ -1,31 +0,0 @@
diff --git a/Cargo.toml b/Cargo.toml
index 0e92acb..d187ebc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,3 @@
-cargo-features = ["profile-rustflags"]
-
[workspace]
members = [
"warpgate",
@@ -160,20 +158,2 @@
[profile.coverage]
inherits = "dev"
-
-[profile.dev.package.aws-sdk-ec2]
-hint-mostly-unused = true
-
-[profile.release.package.aws-sdk-ec2]
-hint-mostly-unused = true
-
-[profile.dev.package.aws-sdk-rds]
-hint-mostly-unused = true
-
-[profile.release.package.aws-sdk-rds]
-hint-mostly-unused = true
-
-[profile.dev.package.aws-sdk-eks]
-hint-mostly-unused = true
-
-[profile.release.package.aws-sdk-eks]
-hint-mostly-unused = true

View File

@@ -1,15 +1,15 @@
diff --git a/package.json b/package.json
index 0f1d768..c070a59 100644
index d8734a74..513d5606 100644
--- a/package.json
+++ b/package.json
@@ -12,8 +12,8 @@
"postinstall": "npm run openapi:client:gateway && npm run openapi:client:admin",
"openapi:schema:gateway": "cargo run -p warpgate-protocol-http > src/gateway/lib/openapi-schema.json",
"openapi:schema:admin": "cargo run -p warpgate-admin > src/admin/lib/openapi-schema.json",
- "openapi:client:gateway": "openapi-generator-cli generate -g typescript-fetch -i src/gateway/lib/openapi-schema.json -o src/gateway/lib/api-client -p npmName=warpgate-gateway-api-client -p useSingleRequestParameter=true && cd src/gateway/lib/api-client && npm i typescript@5 && npm i && npx tsc --target esnext --module esnext && rm -rf src tsconfig.json",
- "openapi:client:admin": "openapi-generator-cli generate -g typescript-fetch -i src/admin/lib/openapi-schema.json -o src/admin/lib/api-client -p npmName=warpgate-admin-api-client -p useSingleRequestParameter=true && cd src/admin/lib/api-client && npm i typescript@5 && npm i && npx tsc --target esnext --module esnext && rm -rf src tsconfig.json",
+ "openapi:client:gateway": "openapi-generator-cli generate -g typescript-fetch -i src/gateway/lib/openapi-schema.json -o src/gateway/lib/api-client -p npmName=warpgate-gateway-api-client -p useSingleRequestParameter=true && ln -sr node_modules src/gateway/lib/api-client/node_modules && cd src/gateway/lib/api-client && npx tsc --target esnext --moduleResolution node && npx tsc -p tsconfig.esm.json --target esnext --moduleResolution node && rm -rf src tsconfig.json tsconfig.esm.json",
+ "openapi:client:admin": "openapi-generator-cli generate -g typescript-fetch -i src/admin/lib/openapi-schema.json -o src/admin/lib/api-client -p npmName=warpgate-admin-api-client -p useSingleRequestParameter=true && ln -sr node_modules src/admin/lib/api-client/node_modules && cd src/admin/lib/api-client && npx tsc --target esnext --moduleResolution node && npx tsc -p tsconfig.esm.json --target esnext --moduleResolution node && rm -rf src tsconfig.json tsconfig.esm.json",
"openapi:tests-sdk": "openapi-generator-cli generate -g python -i src/admin/lib/openapi-schema.json -o ../tests/api_sdk",
"openapi": "npm run openapi:schema:admin && npm run openapi:schema:gateway && npm run openapi:client:admin && npm run openapi:client:gateway"
},
@@ -16,8 +16,8 @@
"postinstall": "npm run openapi:client:gateway && npm run openapi:client:admin",
"openapi:schema:gateway": "cargo run -p warpgate-protocol-http > src/gateway/lib/openapi-schema.json",
"openapi:schema:admin": "cargo run -p warpgate-admin > src/admin/lib/openapi-schema.json",
- "openapi:client:gateway": "openapi-generator-cli generate -g typescript-fetch -i src/gateway/lib/openapi-schema.json -o src/gateway/lib/api-client -p npmName=warpgate-gateway-api-client -p useSingleRequestParameter=true && cd src/gateway/lib/api-client && npm i typescript@5 && npm i && npx tsc --target esnext --module esnext && rm -rf src tsconfig.json",
- "openapi:client:admin": "openapi-generator-cli generate -g typescript-fetch -i src/admin/lib/openapi-schema.json -o src/admin/lib/api-client -p npmName=warpgate-admin-api-client -p useSingleRequestParameter=true && cd src/admin/lib/api-client && npm i typescript@5 && npm i && npx tsc --target esnext --module esnext && rm -rf src tsconfig.json",
+ "openapi:client:gateway": "openapi-generator-cli generate -g typescript-fetch -i src/gateway/lib/openapi-schema.json -o src/gateway/lib/api-client -p npmName=warpgate-gateway-api-client -p useSingleRequestParameter=true && ln -sr node_modules src/gateway/lib/api-client/node_modules && cd src/gateway/lib/api-client && npx tsc --target esnext --moduleResolution node && npx tsc -p tsconfig.esm.json --target esnext --moduleResolution node && rm -rf src tsconfig.json tsconfig.esm.json",
+ "openapi:client:admin": "openapi-generator-cli generate -g typescript-fetch -i src/admin/lib/openapi-schema.json -o src/admin/lib/api-client -p npmName=warpgate-admin-api-client -p useSingleRequestParameter=true && ln -sr node_modules src/admin/lib/api-client/node_modules && cd src/admin/lib/api-client && npx tsc --target esnext --moduleResolution node && npx tsc -p tsconfig.esm.json --target esnext --moduleResolution node && rm -rf src tsconfig.json tsconfig.esm.json",
"openapi:tests-sdk": "openapi-generator-cli generate -g python -i src/admin/lib/openapi-schema.json -o ../tests/api_sdk",
"openapi": "npm run openapi:schema:admin && npm run openapi:schema:gateway && npm run openapi:client:admin && npm run openapi:client:gateway"
},