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
20 changes: 17 additions & 3 deletions src/BenchmarksApps/TLS/Kestrel/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,15 @@ bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509
{
logged = true;

var tlsHandshakeFeature = context.Features.GetRequiredFeature<ITlsHandshakeFeature>();
var tlsFeature = context.Features.GetRequiredFeature<ITlsHandshakeFeature>();

Console.WriteLine("Request details:");
Console.WriteLine("-----");
Console.WriteLine("TLS: " + tlsHandshakeFeature.Protocol);
Console.WriteLine($"Protocol: {tlsFeature.Protocol}");
Console.WriteLine($"CipherSuite: {tlsFeature.NegotiatedCipherSuite}");
Console.WriteLine($"CipherAlgorithm: {tlsFeature.CipherAlgorithm}");
Console.WriteLine($"KeyExchangeAlgorithm: {tlsFeature.KeyExchangeAlgorithm}");
Console.WriteLine("TLS: " + tlsFeature.Protocol);
Console.WriteLine("-----");
}

Expand Down Expand Up @@ -218,7 +222,7 @@ bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509
}

app.MapGet("/hello-world", () =>
{
{
return Results.Ok("Hello World!");
});

Expand Down Expand Up @@ -246,6 +250,16 @@ bool AllowAnyCertificateValidationWithLogging(X509Certificate2 certificate, X509
{
Console.WriteLine($"\tenabled logging stats to console");
}

if (!(OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()))
{
#pragma warning disable CA1416 // Validate platform compatibility
Console.WriteLine($"OpenSSL: {System.Security.Cryptography.SafeEvpPKeyHandle.OpenSslVersion}");
#pragma warning restore CA1416 // Validate platform compatibility
}

Console.WriteLine($"OPENSSL_CONF: {Environment.GetEnvironmentVariable("OPENSSL_CONF")}");
Console.WriteLine($"LD_LIBRARY_PATH: {Environment.GetEnvironmentVariable("LD_LIBRARY_PATH")}");
Console.WriteLine($"\tlistening endpoints: {listeningEndpoints}");
Console.WriteLine("--------------------------------");

Expand Down
62 changes: 62 additions & 0 deletions src/BenchmarksApps/TLS/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Useful stuff to test TLS behavior

### Docker images
Crank agent comes with its own dockerfile, and its own dependencies. Here we are interested in some low-level setups of TLS parameters on OS level as well.
For that reason in [crank/agent](./crank/agent/) you can find a replica of dockerfiles from [crank](https://github.com/dotnet/crank/tree/main/docker/agent).

### Analysis of TLS parameters on request
To lookup TLS behavior you can install npcap/wireshark on win machine,
and collect a network dump (note: using a custom port requires to use `Analyze->DecodeAs` and set TCP / TLS port on dump data). There in `Client Hello` or `Server Hello` TLS parameters can be found.

However, easier way is to simply perform a curl request
```bash
curl -v https://<ip>:<port>/<endpoint> --tlsv1.3 --tls-max 1.2 --insecure --curves [P-256/P-384/P-521/X25519]
```
where:
- `--insecure` skips certificate check (but still runs with TLS)
- `--tlsv1.3` or `--tlsv1.2` sets a minimum tls version
- `--tls-max 1.3` or `--tls-max 1.2` sets a maximum tls version (does not allow client-server to lift up a version)
- `--curves ...` forces a specific curve.

In output you nee to find SSL connection:
```
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / secp521r1 / RSASSA-PSS
```

### Network dump collection and analysis
Most probably benchmarks are run via CI setup, and client will send it's own request (as set). So you want to collect the network dump on the server to ensure request/response has correct TLS parameters.

In order to collect network dump (via `tcpdump`) use this command. Change the port accordingly.
```bash
sudo tcpdump -i any -w capture.pcap port 5000
```

then you can analyze it via `tshark`
```bash
tshark -r capture.pcap -Y "tls.handshake.type == 2" -d tcp.port==5000,tls -c 300 -V
```
Arguments:
- `-Y "tls.handshake.type == 2"` filters only `Server Hello` packets.
- `-d tcp.port==5000,tls` changes the port for tcp/tls if client/server does not communicate via standard ports.
- `-c 300` looks into only first 300 packets. Otherwise too hard to see in a single cmd window
- `-V` gives verbose infomation about packet (you can see EC, CipherSuite used etc)

### Machine setup
You could use [set-fips-compliant-tls-config](./set-fips-compliant-tls-config.ps1) to configure machine. It may not work (registry on windows does not apply always).

You can set TLS CipherSuite and ECC Curve order in Windows UI:
- Local Group Policy Editor -> Computer Configuration > Administrative Templates > Network > SSL Configuration
- Values can be taken from https://learn.microsoft.com/en-us/windows/win32/secauthn/tls-elliptic-curves-in-windows-10-1607-and-later

### Verify machine setup

#### Windows
- Look cipher suite priority list in registry:
```powershell
(Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002' -Name 'Functions').Functions -split ',' | ForEach-Object { "{0,3}. {1}" -f ($_.ReadCount), $_ }
```

- Look eliptic curves priority list in registry:
```powershell
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Cryptography\Configuration\Local\SSL\00010002' -Name 'EccCurves' -ErrorAction SilentlyContinue
```
118 changes: 118 additions & 0 deletions src/BenchmarksApps/TLS/crank/agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env

COPY . .

ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1

# Build self contained
RUN dotnet publish -c Release src/Microsoft.Crank.Agent --output /app --framework net8.0

# Build runtime image
# FROM mcr.microsoft.com/dotnet/aspnet:8.0
# Use SDK image as it is required for the dotnet tools
FROM mcr.microsoft.com/dotnet/sdk:8.0

ARG CPUNAME=x86_64
ARG ENABLE_FIPS_MODE=false
ARG OPENSSL_CIPHER_STRING=TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
ARG OPENSSL_GROUPS=P-384:P-256:P-521

# Install dotnet-symbols
RUN dotnet tool install -g dotnet-symbol
ENV PATH="${PATH}:/root/.dotnet/tools"

# Install dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
git \
procps \
cgroup-tools \
curl \
wget \
nano \
# dotnet performance repo microbenchmark dependencies
libgdiplus \
# libmsquic requirements
gnupg2 \
software-properties-common \
# NativeAOT requirements
clang \
zlib1g-dev \
libkrb5-dev \
# .NET 9.0 requirement
libc6

# Install HTTP/3 support
RUN curl -LO https://packages.microsoft.com/keys/microsoft.asc && \
echo 2fa9c05d591a1582a9aba276272478c262e95ad00acf60eaee1644d93941e3c6 microsoft.asc| sha256sum --check - && \
apt-key add microsoft.asc && \
rm microsoft.asc && \
echo deb https://packages.microsoft.com/debian/12/prod bookworm main >> /etc/apt/sources.list.d/microsoft.list && \
apt-get update && \
apt-get install -y libmsquic && \
rm -rf /var/lib/apt/lists/*

# Build and install h2load. Required as there isn't a way to distribute h2load as a single file to download
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
g++ make binutils autoconf automake autotools-dev libtool pkg-config \
zlib1g-dev libcunit1-dev libxml2-dev libev-dev libevent-dev libjansson-dev \
libc-ares-dev libjemalloc-dev libsystemd-dev \
python-is-python3 python3-dev python3-setuptools

ENV DEBIAN_FRONTEND=noninteractive
# Add the Debian sid repository
RUN echo 'deb http://deb.debian.org/debian sid main' >> /etc/apt/sources.list \
&& echo 'deb http://deb.debian.org/debian-debug sid-debug main' >> /etc/apt/sources.list

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
openssl libssl-dev openssl-dbgsym libssl3t64-dbgsym \
&& openssl version

# Configure OpenSSL for FIPS-compliant cipher suites if $ENABLE_FIPS_MODE
RUN if [ "$ENABLE_FIPS_MODE" = "true" ]; then \
echo "=== FIPS MODE ENABLED - Configuring OpenSSL ===" && \
cat /etc/ssl/openssl.cnf && \
echo "" >> /etc/ssl/openssl.cnf && \
echo "openssl_conf = openssl_init" >> /etc/ssl/openssl.cnf && \
echo "[openssl_init]" >> /etc/ssl/openssl.cnf && \
echo "ssl_conf = ssl_sect" >> /etc/ssl/openssl.cnf && \
echo "[ssl_sect]" >> /etc/ssl/openssl.cnf && \
echo "system_default = system_default_sect" >> /etc/ssl/openssl.cnf && \
echo "[system_default_sect]" >> /etc/ssl/openssl.cnf && \
echo "CipherString = $OPENSSL_CIPHER_STRING" >> /etc/ssl/openssl.cnf && \
echo "Groups = $OPENSSL_GROUPS" >> /etc/ssl/openssl.cnf && \
echo "=== FIPS Configuration Applied ===" && \
tail -15 /etc/ssl/openssl.cnf; \
else \
echo "=== FIPS MODE DISABLED ==="; \
fi

# If nghttp2 build fail just ignore it
ENV NGHTTP2_VERSION=1.58.0
RUN cd /tmp \
&& curl -L "https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2_VERSION}/nghttp2-${NGHTTP2_VERSION}.tar.gz" -o "nghttp2-${NGHTTP2_VERSION}.tar.gz" \
&& tar -zxvf "nghttp2-${NGHTTP2_VERSION}.tar.gz" \
&& cd /tmp/nghttp2-$NGHTTP2_VERSION \
&& ./configure \
&& make \
&& make install || true

# Install docker client
ENV DOCKER_VERSION=17.09.0-ce
RUN cd /tmp \
&& curl "https://download.docker.com/linux/static/stable/${CPUNAME}/docker-${DOCKER_VERSION}.tgz" -o docker.tgz \
&& tar xvzf docker.tgz \
&& cp docker/docker /usr/bin \
&& rm -rf docker.tgz docker

# Install perfcollect
ADD https://raw.githubusercontent.com/microsoft/perfview/main/src/perfcollect/perfcollect /usr/bin/perfcollect
RUN chmod +x /usr/bin/perfcollect
RUN /usr/bin/perfcollect install

COPY --from=build-env /app /app

ENTRYPOINT [ "/app/crank-agent" ]
107 changes: 107 additions & 0 deletions src/BenchmarksApps/TLS/crank/agent/Dockerfile.AzureLinux3
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0-azurelinux3.0 AS build-env

COPY . .

ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1

# Build self contained
RUN dotnet publish -c Release src/Microsoft.Crank.Agent --output /app --framework net8.0

# Build runtime image
# FROM mcr.microsoft.com/dotnet/aspnet:8.0
# Use SDK image as it is required for the dotnet tools
FROM mcr.microsoft.com/dotnet/sdk:8.0-azurelinux3.0

ARG CPUNAME=x86_64
ARG ENABLE_FIPS_MODE=false
ARG OPENSSL_CIPHER_STRING=TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
ARG OPENSSL_GROUPS=P-384:P-256:P-521

# Install dotnet-symbols
RUN dotnet tool install -g dotnet-symbol
ENV PATH="${PATH}:/root/.dotnet/tools"

# Install dependencies
RUN tdnf update -y \
&& tdnf install -y \
git \
procps-ng \
curl \
wget \
libcgroup \
libcgroup-tools \
# dotnet performance repo microbenchmark dependencies
libgdiplus \
# libmsquic requirements
gnupg2 \
# NativeAOT requirements
clang \
zlib-devel \
krb5-devel \
# .NET 9.0 requirement
glibc

# Install HTTP/3 support
RUN tdnf install -y libmsquic

# Build and install h2load. Required as there isn't a way to distribute h2load as a single file to download
RUN tdnf install -y \
gcc-c++ make binutils autoconf automake libtool pkg-config \
zlib-devel cunit-devel libxml2-devel libev-devel libevent-devel jansson-devel \
c-ares-devel jemalloc-devel systemd-devel \
python3-devel python3-setuptools

# ENV OPENSSL_VERSION=3.3.3 Version pinning does not work the same way in AL3 as it does in Debian/Ubuntu. Cannot use * in version, so we will use the latest version available in the repository.
RUN tdnf install -y \
openssl openssl-devel \
&& tdnf clean all

# Configure OpenSSL for FIPS-compliant cipher suites if $ENABLE_FIPS_MODE
RUN if [ "$ENABLE_FIPS_MODE" = "true" ]; then \
echo "=== FIPS MODE ENABLED - Configuring OpenSSL ===" && \
cat /etc/pki/tls/openssl.cnf && \
echo "" >> /etc/pki/tls/openssl.cnf && \
echo "openssl_conf = openssl_init" >> /etc/pki/tls/openssl.cnf && \
echo "[openssl_init]" >> /etc/pki/tls/openssl.cnf && \
echo "ssl_conf = ssl_sect" >> /etc/pki/tls/openssl.cnf && \
echo "[ssl_sect]" >> /etc/pki/tls/openssl.cnf && \
echo "system_default = system_default_sect" >> /etc/pki/tls/openssl.cnf && \
echo "[system_default_sect]" >> /etc/pki/tls/openssl.cnf && \
echo "CipherString = $OPENSSL_CIPHER_STRING" >> /etc/pki/tls/openssl.cnf && \
echo "Groups = $OPENSSL_GROUPS" >> /etc/pki/tls/openssl.cnf && \
echo "=== FIPS Configuration Applied ===" && \
tail -15 /etc/pki/tls/openssl.cnf; \
else \
echo "=== FIPS MODE DISABLED ==="; \
fi

# If nghttp2 build fail just ignore it
ENV NGHTTP2_VERSION=1.58.0
RUN tdnf install -y \
glibc-devel gawk kernel-headers

RUN cd /tmp \
&& curl -L "https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2_VERSION}/nghttp2-${NGHTTP2_VERSION}.tar.gz" -o "nghttp2-${NGHTTP2_VERSION}.tar.gz" \
&& tar -zxvf "nghttp2-${NGHTTP2_VERSION}.tar.gz" \
&& cd /tmp/nghttp2-$NGHTTP2_VERSION \
&& ./configure \
&& make \
&& make install || true

# Install docker client
ENV DOCKER_VERSION=17.09.0-ce
RUN cd /tmp \
&& curl "https://download.docker.com/linux/static/stable/${CPUNAME}/docker-${DOCKER_VERSION}.tgz" -o docker.tgz \
&& tar xvzf docker.tgz \
&& cp docker/docker /usr/bin \
&& rm -rf docker.tgz docker

# Install perfcollect
ADD https://raw.githubusercontent.com/microsoft/perfview/main/src/perfcollect/perfcollect /usr/bin/perfcollect
RUN chmod +x /usr/bin/perfcollect
RUN /usr/bin/perfcollect install

COPY --from=build-env /app /app

ENTRYPOINT [ "/app/crank-agent" ]
25 changes: 25 additions & 0 deletions src/BenchmarksApps/TLS/crank/agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Crank Agent image for TLS tests

...

### Crontab configuration

Machines are configured to perform a git pull, get latest changes, rebuild crank and restart it once in a while. On linux machines it is happening via `cron`.
In order to be able to use Dockerfile from this folder (`/crank/agent`), one should change the crontab.

To lookup crontab configured:
```
sudo crontab -l
```

You can use crontab like this:
```
0 0 * * * cd /home/dotnetperfuser/src/crank/docker/agent; ./stop.sh; docker rm -f $(docker ps -a -q --filter "label=benchmarks"); docker system prune --all --force --volumes; git checkout -f main; git reset --hard; git pull; cd /home/dotnetperfuser/src/Benchmarks; git checkout -f main; git reset --hard; git pull; cp -rf /home/dotnetperfuser/src/Benchmarks/src/BenchmarksApps/TLS/crank/agent/* /home/dotnetperfuser/src/crank/docker/agent/; cd /home/dotnetperfuser/src/crank/docker/agent; ./build.sh <arguments>; ./run.sh <arguments>
```

Cron tab does the following:
1) fetches the latest dotnet/crank
2) fetches latest aspnetcore/Benchmarks
3) copies dockerfile from aspnetcore/Benchmarks into dotnet/crank
4) builds and runs the crank agent using custom Dockerfile

Loading
Loading