-
Notifications
You must be signed in to change notification settings - Fork 250
debug SecureRandom #999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
debug SecureRandom #999
Conversation
which I do not see locally. |
|
https://github.com/openjdk/jdk21u-dev/blob/bad21fbe258402e7697279fdbdf7d67e02d20c03/src/java.base/windows/native/libjava/WinCAPISeedGenerator.c#L49C13-L52 alas this code does not call Could be some missing libraries in the container, could be permissions could be something entirely different... |
|
Cc @dduportal / @lemeurherve otherwise I’ll check later on. |
|
I would guess it is either |
|
I don’t think it’s in a container but will check when back to my laptop |
And here is how it's provisioned: https://github.com/jenkins-infra/packer-images/blob/main/provisioning/windows-provision.ps1 |
Adding to that, the default ec2 AMIs: https://github.com/jenkins-infra/jenkins-infra/blob/f67283f8dc6dac49954ba5407796bce2e1562eb8/hieradata/common.yaml#L222-L228, provisioned from https://github.com/jenkins-infra/packer-images cf comment above |
|
I logged into a running windows VM and ran: pwsh
cd C:\tools\jdk-17\bin
$url = 'https://gist.githubusercontent.com/timja/18b75ae57ecc2d517a8fce0811c98bdd/raw/83c73c7a59a8a3ef3b094166cdb9b35bf51ce7cb/gistfile1.txt'
Invoke-WebRequest $url -Outfile Main.java
.\javac Main.java
.\java '-Djava.security.debug="provider"' MainI tried it on Java 25 as well with the same result. |
|
I think best to just write a little C program and run it on the VM. |
|
Going to try this: |
|
This code (from mslearn on how to use the API): #include <Windows.h>
#include <wincrypt.h>
#include <stdio.h>
int main() {
printf("Debug Secure Random 2\n");
//-------------------------------------------------------------------
// Declare and initialize variables.
HCRYPTPROV hCryptProv = NULL; // handle for a cryptographic
// provider context
LPCSTR UserName = "J2SETest"; // name of the key container
// to be used
//-------------------------------------------------------------------
// Attempt to acquire a context and a key
// container. The context will use the default CSP
// for the RSA_FULL provider type. DwFlags is set to zero
// to attempt to open an existing key container.
if (CryptAcquireContext(
&hCryptProv, // handle to the CSP
UserName, // container name
NULL, // use the default provider
PROV_RSA_FULL, // provider type
0)) // flag values
{
printf("A cryptographic context with the %s key container \n",
UserName);
printf("has been acquired.\n\n");
}
else
{
//-------------------------------------------------------------------
// An error occurred in acquiring the context. This could mean
// that the key container requested does not exist. In this case,
// the function can be called again to attempt to create a new key
// container. Error codes are defined in Winerror.h.
DWORD errCode = GetLastError();
char errMsg[512];
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errCode,
0,
errMsg,
sizeof(errMsg),
NULL);
if (errCode == NTE_BAD_KEYSET)
{
if (CryptAcquireContext(
&hCryptProv,
UserName,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))
{
printf("A new key container has been created.\n");
}
else
{
DWORD createErrCode = GetLastError();
char createErrMsg[512];
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
createErrCode,
0,
createErrMsg,
sizeof(createErrMsg),
NULL);
printf("Could not create a new key container. Error %lu: %s\n", createErrCode, createErrMsg);
fflush(stdout);
exit(1);
}
}
else
{
printf("A cryptographic service handle could not be acquired. Error %lu: %s\n", errCode, errMsg);
fflush(stdout);
exit(1);
}
} // End of else.
//-------------------------------------------------------------------
// A cryptographic context and a key container are available. Perform
// any functions that require a cryptographic provider handle.
//-------------------------------------------------------------------
// When the handle is no longer needed, it must be released.
if (CryptReleaseContext(hCryptProv, 0))
{
printf("The handle has been released.\n");
}
else
{
printf("The handle could not be released.\n");
}
}Works on my windows machine and fails on the VM with: |
|
Another iteration: |
|
The error handling there is questionable (no better then the JDK!l |
|
I've copiloted this: DWORD errCode = GetLastError();
char errMsg[512];
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errCode,
0,
errMsg,
sizeof(errMsg),
NULL);
printf("Could not create a new key container. Error %lu: %s\n", errCode, errMsg);
exit(1);trying now |
|
|
The user is running as an admin: PS Z:\jenkins\debug> $user = [Security.Principal.WindowsIdentity]::GetCurrent();
PS Z:\jenkins\debug> (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
True |
PS Z:\jenkins\debug> icacls "$env:APPDATA\Microsoft\Crypto\RSA"
Z:\jenkins\AppData\Roaming\Microsoft\Crypto\RSA NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Administrators:(I)(OI)(CI)(F)
EC2AMAZ-44RG0FU\jenkins:(I)(OI)(CI)(F)
Successfully processed 1 files; Failed processing 0 files |
|
With a different key container name: |
|
With more error handling: |
|
Hmm weird I thought I'd try on windows-2022 but my exe doesn't even run there =/ no error message or anything but nothing is outputted |
|
Here's the output running on windows 2022 anyway: https://ci.jenkins.io/job/Plugins/job/credentials-plugin/job/PR-999/8/pipeline-overview/log?nodeId=6 |
missing the specific vc runtime? |
|
Are you logged in via RDP to the console (for servers historically you needed to use the |
I'm logged in over SSH (which is the same as Jenkins is doing)
Probably |
|
New version should be statically linked: |
I'm now playing around with using the VM hostname instead of its IPv4 |
Using the hostname does not work either: same behavior. I'm going back to the password init in noninteractive |
|
AH I might have found a technique with the |
|
YEEEPEEKAY https://ci.jenkins.io/job/Infra/job/acceptance-tests/job/infra-checks/736/console |
|
Nice! Is Posh-SSH needed? Isn't the fix to do an SSH to localhost when the user is created using password auth first and then key based auth? The server should have SSH pre-installed on it? I guess it makes it easier to e.g. work with a password and use powershell objects |
Could be. I focused on validating the "patch" (e.g. verifying that the CryptoAPI can be used in the pipeline by performing a first SSH login with password in non interactive) and I wanted to rule out the "how to pass password to ssh client".
Exactly, that's why I started with it.
Absolutely. But the sequence of task is important:
|
|
So I'm not clear if the fix is just "ssh with user:pass" or if it you need to "run something that sets up the crypt API that has a user/PW token" If it's the latter then start-process will probably work to just run out debug tool and not need poshssh |
I initially thought we ran this scenario (setup CryptoAPI with the |
|
you don't reallay need to. just check the manually with |
Actually I do: it does not seem to do anything at all. I'm not sure what i'm missing in the call. generates the log files but they are empty (both of them). I can't even get an exit code. What is wrong with my instruction? |
|
|
I'm retrying to see what we can do. |
:'( |
|
Don’t we have 7 installed? |
We're ensuring both are installed in packer images (at least): https://github.com/jenkins-infra/packer-images/blob/317e2015fd5d9ee2670a6da01ada4b2700d953d8/provisioning/windows-provision.ps1#L381-L394 |
|
Oh right, I juste have to use |
Doesn't support password auth for SSH 😮💨 (edit: not as a non-interactive script - https://learn.microsoft.com/en-us/powershell/scripting/security/remoting/ssh-remoting-in-powershell) |
|
I give up: I'll go with the Posh-SSH module.
I've exhausted both my skills and patience: if no one objects, I'll go forward with the Posh-SSH for an initial implementation. |
|
Gotcha, I finally succeeded with the native |
Here is the userdata code applied to to the agent template
User data code: Click to expand# Requires using YAML for the Windows "Cloud Init" stuff. Multipart upload of a powershell script does not work.
version: 1.1
tasks:
- task: executeScript
inputs:
- frequency: always
type: powershell
runAs: localSystem
content: |-
## Set up permissions context (as you are Administrator here)
Set-ExecutionPolicy Unrestricted -Scope LocalMachine -Force -ErrorAction Ignore
# Don't set this before Set-ExecutionPolicy as it throws an error
$ErrorActionPreference = "stop"
## Setup datadog
(Get-Content C:\ProgramData\Datadog\datadog.yaml -Raw) -Replace 'api_key:', 'api_key: <redacted>' | Set-Content C:\ProgramData\Datadog\datadog.yaml
& "$env:ProgramFiles\Datadog\Datadog Agent\bin\agent.exe" restart-service
Write-Output 'Datadog service setup'
Get-Date
## Disable WinRM
Remove-Item -Path WSMan:\Localhost\listener\listener* -Recurse
cmd.exe /c net stop winrm
Write-Output 'WinRM disabled'
Get-Date
## Setup NVMe(s) and map it to the Z: drive
$nb = Get-Disk | Where-Object PartitionStyle -eq 'RAW' | tee -Variable Disks | measure
Write-Output "$nb.Count disk found."
Get-Date
Switch ($nb.Count)
{
0 {Write-Output "No RAW disk found."}
1 {
$Disks | Initialize-Disk -PartitionStyle MBR
$Disks | New-Partition -UseMaximumSize -MbrType IFS
$Partition = Get-Partition -DiskNumber $Disks.Number
$Partition | Format-Volume -FileSystem NTFS -Confirm:$false
$Partition | Add-PartitionAccessPath -AccessPath "Z:"
Get-WmiObject Win32_Volume | Format-Table Name, Label, FreeSpace, Capacity
}
default {
$Disks | ForEach-Object -Begin {Get-Date} -Process {
Initialize-Disk -PartitionStyle MBR -PassThru -DiskNumber $_.Number
New-Partition -UseMaximumSize -MbrType IFS
$Partition = Get-Partition -DiskNumber $_.Number
$Partition | Format-Volume -FileSystem NTFS -Confirm:$false
$Partition | Add-PartitionAccessPath -AccessPath "Z:"
} -End {Get-Date}
Get-WmiObject Win32_Volume | Format-Table Name, Label, FreeSpace, Capacity
}
}
Write-Output 'Disk setup finished.'
Get-Date
## Setup Docker Engine
$dockerGroup = 'docker-users'
try {Get-LocalGroup -Name $dockerGroup;} catch {New-LocalGroup -Name $dockerGroup;}
# Note: file path MUST use Unix-style separator (/)
@"
{
"hosts": ["npipe://"],
"data-root": "Z:/docker",
"group": "$dockerGroup"
}
"@ | Set-Content C:\ProgramData\Docker\config\daemon.json
# Restart docker engine
Restart-Service docker
docker info
Write-Output 'Docker Engine setup finished.'
Get-Date
# Create the 'jenkins' agent's user account
$username = 'jenkins'
## TODO: generate random password
$userPassword = '<redacted>'
$pw = ConvertTo-SecureString -String $userPassword -AsPlainText -Force
New-LocalUser -Name $username -Password $pw
$sshGroup = "openssh users"
try {Get-LocalGroup -Name $sshGroup;} catch {New-LocalGroup -Name $sshGroup;}
Add-LocalGroupMember -Group $sshGroup -Member $username
Add-LocalGroupMember -Group $dockerGroup -Member $username
Write-Output "User $username created."
Get-Date
# Set up Windows default Users profiles location to the custom data disk drive
$userpath = 'Z:\Users'
$regpath = 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList'
$regname = 'ProfilesDirectory'
Set-ItemProperty -path $regpath -name $regname -value $userpath
Write-Output "Set up default user profiles to $userpath"
Get-Date
$cryptoDebugUrl = 'https://github.com/user-attachments/files/24455803/debug-secure-random-2.zip'
$cryptoBaseDir = 'C:'
$cryptoBasename = "$cryptoBaseDir\debug-secure-random-2"
$cryptoExe = "$cryptoBasename.exe"
$cryptoArchive = "$cryptoBasename.zip"
Invoke-WebRequest $cryptoDebugUrl -OutFile $cryptoArchive
Expand-Archive "$cryptoArchive" -DestinationPath "$cryptoBaseDir\"
Write-Output "Debug Tool to setup CryptoApi deployed to $cryptoExe"
Get-Date
$env:DISPLAY = '0'
$env:SSH_ASKPASS_REQUIRE = 'force'
$env:SSH_ASKPASS = 'C:\askpass.bat'
@"
@echo off
echo $userPassword
"@ | Set-Content $env:SSH_ASKPASS
ssh $username@localhost -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null "$cryptoExe"
Remove-Item -Path "$env:SSH_ASKPASS" -Force
Remove-Item -Path "$cryptoExe" -Force
Remove-Item -Path "$cryptoArchive" -Force
Remove-Item -Path 'C:\goss-windows-2019.yaml' -Force
Remove-Item -Path 'C:\goss-windows-2022.yaml' -Force
Remove-Item -Path 'C:\addSSHPubKey.ps1' -Force
Write-Output 'Initialized User Profile and Crypto through SSH with password authentication'
Get-Date
# Setup SSH key for user in its profile
$userSSHDir = "$userpath\$username\.ssh"
New-Item -ItemType Directory -Path "$userSSHDir" -Force | Out-Null
$authorizedKeysFile = "$userSSHDir\authorized_keys"
$keyUrl = 'https://raw.githubusercontent.com/jenkins-infra/aws/main/ec2_agents_authorized_keys'
Write-Host "Downloading SSH key from $keyUrl to $authorizedKeysFile"
Invoke-WebRequest $keyUrl -OutFile $authorizedKeysFile
## Mark cloud init as finished using a marker file
New-Item -Path "Z:/Temp" -ItemType "Directory"
New-Item -Path "Z:/Temp/.cloud-init.done" -ItemType "File" -Value "Cloud Init"Build replay testing this agent template: https://ci.jenkins.io/job/Plugins/job/credentials-plugin/job/PR-999/22/ with no Linux and a Windows JDK17 custom platform: |
Ref. PowerShell/Win32-OpenSSH#2420 and jenkinsci/credentials-plugin#999 Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
|
Update: adding back the Retrying with https://ci.jenkins.io/job/Plugins/job/credentials-plugin/job/PR-999/23 |
|
All this work is an absolute failure: we still have the same message about system seed generator (twice) in https://ci.jenkins.io/job/Plugins/job/credentials-plugin/job/PR-999/23: (edit)
|
you have java installed so you can probably run something like the following converted to powershell: echo java.security.SecureRandom.getInstanceStrong().generateSeed(1) | jshellI've now forgotten if we need |
CryptoAPI in the administrators group behaves differently to non administrators. |
Do not do this as part of the pipeline library, this is wrong. - either do this when you install Git4win, or configure the |
I agree on principle: we should not do this. But changing this right now means we should first go back in history on why it was put here in first place (I guess a need for quick fix + missing knowledge + a spread of Windows configurations which is not the case nowadays). If that's ok for you, we can continue in that part (e.g. dropping the admin permission) but expect a bit of delays then 😅 (we have an incoming 2.541.1 LTS this week . Proposed plan (does it look good to you):
|
Ref. PowerShell/Win32-OpenSSH#2420 and jenkinsci/credentials-plugin#999 Signed-off-by: Damien Duportal <damien.duportal@gmail.com>


diagnosis for #995 (comment) / jenkins-infra/helpdesk#4939
Testing done
Submitter checklist