mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Fix cert issues in Virtual Environments
This commit is contained in:
parent
9c55a95eb9
commit
f5af26092a
3 changed files with 61 additions and 22 deletions
|
|
@ -12,17 +12,6 @@ export function getCaCertPath() {
|
||||||
return path.join(certFolder, "ca-cert.pem");
|
return path.join(certFolder, "ca-cert.pem");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {forge.pki.PublicKey} publicKey
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function createKeyIdentifier(publicKey) {
|
|
||||||
return forge.pki.getPublicKeyFingerprint(publicKey, {
|
|
||||||
encoding: "binary",
|
|
||||||
md: forge.md.sha1.create(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} hostname
|
* @param {string} hostname
|
||||||
* @returns {{privateKey: string, certificate: string}}
|
* @returns {{privateKey: string, certificate: string}}
|
||||||
|
|
@ -62,19 +51,39 @@ export function generateCertForHost(hostname) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
extKeyUsage serverAuth is required for TLS server authentication.
|
Extended Key Usage (EKU) serverAuth extension
|
||||||
This is especially important for Python venv environments, which use their own
|
|
||||||
certificate validation logic and will reject certificates lacking the serverAuth EKU.
|
Needed for TLS server authentication. This extension indicates the certificate's
|
||||||
Adding serverAuth does not impact other usages
|
public key may be used for TLS WWW server authentication.
|
||||||
|
Python virtualenv environments (like pipx-installed Poetry) enforce this strictly
|
||||||
|
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12
|
||||||
*/
|
*/
|
||||||
name: "extKeyUsage",
|
name: "extKeyUsage",
|
||||||
serverAuth: true,
|
serverAuth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
Subject Key Identifier (SKI)
|
||||||
|
|
||||||
|
Needed for Python virtualenv SSL validation and certificate chain building.
|
||||||
|
This extension provides a means of identifying certificates containing a particular public key.
|
||||||
|
Python virtualenv environments require this for proper certificate chain validation.
|
||||||
|
System Python installations may be more lenient.
|
||||||
|
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2
|
||||||
|
*/
|
||||||
name: "subjectKeyIdentifier",
|
name: "subjectKeyIdentifier",
|
||||||
subjectKeyIdentifier: createKeyIdentifier(cert.publicKey),
|
subjectKeyIdentifier: createKeyIdentifier(cert.publicKey),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
Authority Key Identifier (AKI)
|
||||||
|
|
||||||
|
Needed for Python virtualenv SSL validation and certificate path validation.
|
||||||
|
This extension identifies the public key corresponding to the private key used to sign
|
||||||
|
this certificate. It links this certificate to its issuing CA certificate.
|
||||||
|
Without this, Python virtualenv certificate validation might fail
|
||||||
|
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.1
|
||||||
|
*/
|
||||||
name: "authorityKeyIdentifier",
|
name: "authorityKeyIdentifier",
|
||||||
keyIdentifier: authorityKeyIdentifier,
|
keyIdentifier: authorityKeyIdentifier,
|
||||||
},
|
},
|
||||||
|
|
@ -142,7 +151,7 @@ function generateCa() {
|
||||||
|
|
||||||
const attrs = [{ name: "commonName", value: "safe-chain proxy" }];
|
const attrs = [{ name: "commonName", value: "safe-chain proxy" }];
|
||||||
cert.setSubject(attrs);
|
cert.setSubject(attrs);
|
||||||
cert.setIssuer(attrs);
|
cert.setIssuer(attrs); // Self-signed: issuer === subject
|
||||||
const keyIdentifier = createKeyIdentifier(cert.publicKey);
|
const keyIdentifier = createKeyIdentifier(cert.publicKey);
|
||||||
cert.setExtensions([
|
cert.setExtensions([
|
||||||
{
|
{
|
||||||
|
|
@ -156,10 +165,28 @@ function generateCa() {
|
||||||
digitalSignature: true,
|
digitalSignature: true,
|
||||||
keyEncipherment: true,
|
keyEncipherment: true,
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
|
Subject Key Identifier (SKI)
|
||||||
|
|
||||||
|
Needed for Python virtualenv SSL validation and certificate chain building.
|
||||||
|
This extension provides a means of identifying certificates containing a particular public key.
|
||||||
|
Python virtualenv environments require this for proper certificate chain validation.
|
||||||
|
System Python installations may be more lenient.
|
||||||
|
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
name: "subjectKeyIdentifier",
|
name: "subjectKeyIdentifier",
|
||||||
subjectKeyIdentifier: keyIdentifier,
|
subjectKeyIdentifier: keyIdentifier,
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
|
Authority Key Identifier (AKI)
|
||||||
|
|
||||||
|
Needed for Python virtualenv SSL validation and certificate path validation.
|
||||||
|
This extension identifies the public key corresponding to the private key used to sign
|
||||||
|
this certificate. It links this certificate to its issuing CA certificate.
|
||||||
|
Without this, Python virtualenv certificate validation might fail
|
||||||
|
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.1
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
name: "authorityKeyIdentifier",
|
name: "authorityKeyIdentifier",
|
||||||
keyIdentifier,
|
keyIdentifier,
|
||||||
|
|
@ -172,3 +199,14 @@ function generateCa() {
|
||||||
certificate: cert,
|
certificate: cert,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {forge.pki.PublicKey} publicKey
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function createKeyIdentifier(publicKey) {
|
||||||
|
return forge.pki.getPublicKeyFingerprint(publicKey, {
|
||||||
|
encoding: "binary",
|
||||||
|
md: forge.md.sha1.create(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@ describe("E2E: safe-chain setup-ci command for pip/pip3", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
container = new DockerTestContainer();
|
container = new DockerTestContainer();
|
||||||
await container.start();
|
await container.start();
|
||||||
|
|
||||||
|
// Clear pip cache before each test to ensure fresh downloads through proxy
|
||||||
|
const shell = await container.openShell("zsh");
|
||||||
|
await shell.runCommand("pip3 cache purge");
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ describe("E2E: pip coverage", () => {
|
||||||
|
|
||||||
const installationShell = await container.openShell("zsh");
|
const installationShell = await container.openShell("zsh");
|
||||||
await installationShell.runCommand("safe-chain setup --include-python");
|
await installationShell.runCommand("safe-chain setup --include-python");
|
||||||
|
|
||||||
|
// Clear pip cache before each test to ensure fresh downloads through proxy
|
||||||
|
await installationShell.runCommand("pip3 cache purge");
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
|
@ -118,9 +121,6 @@ describe("E2E: pip coverage", () => {
|
||||||
|
|
||||||
it(`safe-chain blocks installation of malicious Python packages`, async () => {
|
it(`safe-chain blocks installation of malicious Python packages`, async () => {
|
||||||
const shell = await container.openShell("zsh");
|
const shell = await container.openShell("zsh");
|
||||||
// Clear pip cache to ensure network download through proxy
|
|
||||||
await shell.runCommand("pip3 cache purge");
|
|
||||||
|
|
||||||
const result = await shell.runCommand(
|
const result = await shell.runCommand(
|
||||||
"pip3 install --break-system-packages safe-chain-pi-test"
|
"pip3 install --break-system-packages safe-chain-pi-test"
|
||||||
);
|
);
|
||||||
|
|
@ -247,9 +247,6 @@ describe("E2E: pip coverage", () => {
|
||||||
|
|
||||||
it(`pip3 successfully validates certificates for HTTPS downloads`, async () => {
|
it(`pip3 successfully validates certificates for HTTPS downloads`, async () => {
|
||||||
const shell = await container.openShell("zsh");
|
const shell = await container.openShell("zsh");
|
||||||
// Clear cache to force network download through proxy
|
|
||||||
await shell.runCommand("pip3 cache purge");
|
|
||||||
|
|
||||||
const result = await shell.runCommand(
|
const result = await shell.runCommand(
|
||||||
"pip3 install --break-system-packages certifi"
|
"pip3 install --break-system-packages certifi"
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue