Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions misc/images/openssh-static/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

# Build a statically-linked OpenSSH `ssh` client binary using AWS-LC
# as the crypto backend. AWS-LC is a faster, smaller alternative to
# OpenSSL that also supports FIPS 140-3 validation when needed.
# OpenSSL. The static binary (~3-6MB) is the prerequisite for migrating
# environmentd/clusterd to distroless containers, which lack /usr/bin/ssh.
#
# OpenSSH natively supports AWS-LC as a crypto backend (no patches needed).
# See: https://github.com/openssh/openssh-portable/blob/master/INSTALL
#
# To enable FIPS mode, build with: --build-arg AWS_LC_FIPS=1
# (requires Go for the FIPS delocator)
# FIPS support is built separately; see the SEC-236 FIPS draft (it requires a
# FIPS-branch AWS-LC tag and OpenSSH >= 10.0, and is gated on AWS-LC-FIPS 3.x
# completing NIST CMVP validation).
#
# Usage:
# docker build -t openssh-static .
Expand All @@ -26,8 +28,9 @@
FROM ubuntu:noble-20260210.1 AS builder

ARG AWS_LC_VERSION=v1.54.0
ARG AWS_LC_FIPS=0
ARG OPENSSH_VERSION=V_9_9_P2
# OpenSSH >= 10.0 is required: it natively stubs BN_set_flags() for AWS-LC,
# avoiding any need to hand-define the removed BN_FLG_CONSTTIME flag.
ARG OPENSSH_VERSION=V_10_3_P1

RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
autoconf \
Expand All @@ -45,11 +48,9 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
&& rm -rf /var/lib/apt/lists/*

# Build AWS-LC as a static library.
# When AWS_LC_FIPS=1, enables FIPS mode (requires Go for the delocator).
WORKDIR /build/aws-lc
RUN git clone --depth 1 --branch ${AWS_LC_VERSION} https://github.com/aws/aws-lc.git . \
&& cmake -GNinja -B build \
$([ "$AWS_LC_FIPS" = "1" ] && echo "-DFIPS=1") \
-DBUILD_SHARED_LIBS=0 \
-DBUILD_TESTING=OFF \
-DBUILD_TOOL=OFF \
Expand All @@ -63,20 +64,18 @@ RUN git clone --depth 1 --branch ${AWS_LC_VERSION} https://github.com/aws/aws-lc
WORKDIR /build/openssh
RUN git clone --depth 1 --branch ${OPENSSH_VERSION} https://github.com/openssh/openssh-portable.git . \
&& autoreconf \
# OpenSSH >= 10 stubs BN_set_flags() for AWS-LC, so no -DBN_FLG_CONSTTIME
# shim is needed. Use a plain configure + make: a `make CFLAGS=...` override
# would clobber configure's hardening flags. --disable-pkcs11 avoids link
# errors from ssh-pkcs11.c referencing RSA/EC APIs that AWS-LC omits.
&& ./configure \
--with-ssl-dir=/opt/aws-lc \
--with-zlib \
--with-ldflags=-static \
--without-pam \
--without-libedit \
--disable-pkcs11 \
# AWS-LC does not define the legacy OpenSSL BN_FLG_CONSTTIME flag.
# Setting it to 0 satisfies #ifdef checks in OpenSSH source code.
# This is safe: AWS-LC handles constant-time bignum operations
# internally and does not rely on this flag.
# --disable-pkcs11 avoids link errors from ssh-pkcs11.c calling
# RSA_meth_dup/EC_KEY_METHOD_get_sign which AWS-LC does not provide.
&& make -j"$(nproc)" ssh CFLAGS="-DBN_FLG_CONSTTIME=0" \
&& make -j"$(nproc)" ssh \
&& strip ssh

# Verify the binary is not dynamically linked and is functional.
Expand Down
34 changes: 0 additions & 34 deletions src/ssh-util/src/tunnel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,28 +232,6 @@ impl SshTunnelHandle {
}
}

/// Returns true if FIPS mode is enabled via the MZ_FIPS environment variable.
fn fips_mode_enabled() -> bool {
std::env::var("MZ_FIPS").map_or(false, |v| v == "1" || v == "true")
}

/// Writes a temporary SSH config file that restricts algorithms to FIPS 140-3
/// approved choices only. Returns the path to the config file.
fn write_fips_ssh_config(dir: &std::path::Path) -> Result<std::path::PathBuf, anyhow::Error> {
let config_path = dir.join("ssh_config");
let config_contents = "\
# FIPS 140-3 compliant SSH configuration.
# Only NIST-approved algorithms are permitted.
Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
KexAlgorithms ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512
HostKeyAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-256,rsa-sha2-512
PubkeyAcceptedAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-256,rsa-sha2-512,ssh-ed25519
";
fs::write(&config_path, config_contents)?;
Ok(config_path)
}

async fn connect(
config: &SshTunnelConfig,
timeout_config: SshTimeoutConfig,
Expand All @@ -270,14 +248,6 @@ async fn connect(
// Mostly helpful to ensure the file is not accidentally overwritten.
tempfile.set_permissions(std::fs::Permissions::from_mode(0o400))?;

// In FIPS mode, write a restrictive SSH config that only allows
// NIST-approved algorithms.
let fips_config_path = if fips_mode_enabled() {
Some(write_fips_ssh_config(tempdir.path())?)
} else {
None
};

// Try connecting to each host in turn.
let mut connect_err = None;
for host in &config.host {
Expand All @@ -295,10 +265,6 @@ async fn connect(
.server_alive_interval(timeout_config.keepalives_idle)
.connect_timeout(timeout_config.connect_timeout);

if let Some(ref fips_config) = fips_config_path {
builder.config_file(fips_config);
}

match builder.connect_mux(host.clone()).await {
Ok(session) => {
// Delete the private key for safety: since `ssh` still has an open
Expand Down
Loading