Skip to content

Commit

Permalink
fix: pregenerate https certificate
Browse files Browse the repository at this point in the history
node-forge used inside selfsigned is a bit package https://packagephobia.com/result?p=node-forge
Sadly there is no js-only alternative (only `openssl` wrappers).

While testing this feature (server.https: true) I found chrome detects
the certificate is insecure though still allows to access the page with
https after proceeding warning wall.

So as there is not much value in using so big dependency to generate
insecure certificates @patak-dev got an idea to pregenerate single
certificate and publish it with vite.

This need to be tested on various machines though before releasing.

I made the certificate expire in 10 years. Let's cross our fingers this
will be enough.

```
du -ck dist/node
```
before: 12348
after: 9812
  • Loading branch information
TrySound committed Dec 18, 2021
1 parent b9871bb commit 5c727ac
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 480 deletions.
369 changes: 1 addition & 368 deletions packages/vite/LICENSE.md

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions packages/vite/dev-cert.pem
@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAsan51A4X8wLmf/sqn/CiosFSeLnnFG3xUtSspLpudNOAu+Ai
3j8+fYpUknGztKEOZll+o55r2QNyoUgO6n7BVgixxr/iVjvs+SEVWsxYfl/jYZU5
pqJPlzqJKkvKA08kHFSQuXUyPkA0YbjSo1sJInyY2lUAxK3gSKaTUdRRpLO29Bx2
WNOPGGWAjjI85slagiskH+rXabX7Nsp0JMz4ZL0cQHgYBM97VfwumqHOVU+dWzgK
ituQDut17qFndVtQMMhQzRzz8sMb2H9i9YWTr6ateBSKHmNACaMPiYO5w8RXNggh
LhGbxUOCqb4CqB2DKc6QX4ijzdxSD7eYyNgrpQIDAQABAoIBAQCC6iFxpMD5DNYU
0FtkZ8kv7Qx8cWBZqXn980EUUVw7Me7Wgh6vbiL6FwiisK4fF96ohKHQp1gJHYVG
WvbCzIxifR2iupppuNax7TLzKUi8NqQyr+c/ZrMHWNbiF97P+W0+gwHx5u4S+PSg
XbS5MsfUZcOsfp6GTynJ3xUHY1CV76x7YI4tQ5RAWT+EEHihDA4zbh49LoqlTNOE
+pEeOF3bcqAehmLAicEfZU7iUrVKxZ9xpJebRLYBRO/MGEaXf4tp5VcTYgIlyA1p
cx8eAfKiZsiCVpTkY4lBTB9YGhfQqLr7O9aAzeCxTpjY9cJ7tUWEWv2Ybt88SSGa
7iiXLJixAoGBAOSzW6u9oxkTGY1Uy8L8SrXyox3dCwTDdqcS0okboE3dYks84g7M
knrVq3mIh8Kc6SHW2RLaxpPvSGdJywYT+sUEMwF/ZZlIzZEtXOroHgU+2L0y/9Ie
+mu6MBWEdzUWo6AmLK4XGyiqpwqDGpka0P3sai544VamInjjErRX81ebAoGBAMbf
CbnjNg0J05LizUVtXT/9v/PheofXYXEuThpGYi97HgaiOUKgIwn+o0k/o+Wxbgpq
wYTIQNQklUo4/eQbOMqRR1YVy20ylpfJOIS6AJBDKkoLBzyi5IbYspCnd25VYZ+c
MZ1jkmu6qlNXsPxtN9GLrQSluS2r07qTgfM8C92/AoGAGzyVFU3/Z5QTvfY0UiNz
zll4Pa0+i7GvptQiCLwmij9sXmOA1JxecYyS4GV3LaE8TpBkx88QSTv9Fnf+Wtn0
SpRmcq42blvyR3DsmPlN4fgLHfU97EtOLZg988IT5Fmu4PrK5WzRCjXQsPrOJx+/
dg460xAIXiZpApageqPi0jMCgYEAkNxCf28sHg88WFBw6dOiC0zRF9r0oCKZThJG
vgjq2F88HeTpBMkU/ODJHEAJKPU3jMr6r7gW0PDc9jXRaukRUyzD48b4aVBiNapT
SwsaAfuKwGiMGvUktbz/RTNXYb074UGrOlQfge10yESmHRmJgU/W9f2ph8UT82Ij
VIQ2uI0CgYEAzVpC6C/Lx9dVmE6V7evZuE3g67zlMwd7U//X5eKAp0bUnrGTmy/3
5bNHELUrVPEfG4IVIHIb1jTIJHECyTUpw0xDsVzdgsP+BShPBjiJLjMTIy/o53pX
lTNpkrXUtt5OIGC6cMx6nHuWIpfBjMa6vtfAOfzl6tt7o5v3rmaqemU=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIID9zCCAt+gAwIBAgIJQD4HLTmP5m8qMA0GCSqGSIb3DQEBCwUAMGkxFDASBgNV
BAMTC2V4YW1wbGUub3JnMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWEx
EzARBgNVBAcTCkJsYWNrc2J1cmcxDTALBgNVBAoTBFRlc3QxDTALBgNVBAsTBFRl
c3QwHhcNMjExMjE4MTEwMDI2WhcNMzExMjE2MTEwMDI2WjBpMRQwEgYDVQQDEwtl
eGFtcGxlLm9yZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRMwEQYD
VQQHEwpCbGFja3NidXJnMQ0wCwYDVQQKEwRUZXN0MQ0wCwYDVQQLEwRUZXN0MIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsan51A4X8wLmf/sqn/CiosFS
eLnnFG3xUtSspLpudNOAu+Ai3j8+fYpUknGztKEOZll+o55r2QNyoUgO6n7BVgix
xr/iVjvs+SEVWsxYfl/jYZU5pqJPlzqJKkvKA08kHFSQuXUyPkA0YbjSo1sJInyY
2lUAxK3gSKaTUdRRpLO29Bx2WNOPGGWAjjI85slagiskH+rXabX7Nsp0JMz4ZL0c
QHgYBM97VfwumqHOVU+dWzgKituQDut17qFndVtQMMhQzRzz8sMb2H9i9YWTr6at
eBSKHmNACaMPiYO5w8RXNgghLhGbxUOCqb4CqB2DKc6QX4ijzdxSD7eYyNgrpQID
AQABo4GhMIGeMAsGA1UdDwQEAwIC9DAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYB
BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDCDBcBgNVHREEVTBTgglsb2NhbGhvc3SC
FWxvY2FsaG9zdC5sb2NhbGRvbWFpboIGbHZoLm1lgggqLmx2aC5tZYIFWzo6MV2H
BH8AAAGHEP6AAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEBABNxJ19s
U95jrFFaH0So4mOJqtnLlm4eCCOjAhLv7r+Pyqy4ZKoXwagopk46ehx5W888lDin
Zzp2ij1o6Ry286lldT/wf8SOlWRejkb61/R8FMqu9homnD+UY3UCoLxNbkk/j9jo
xkFJPFv0RZQp0gR+F+zcvM+P62cwyLv4C5JcC2JV8GsOdg8io1RJpUxkl/SDHPXy
v7xmJuW7Tzp163adOZSack2EfFYAEI5XCzacIR9Je2vclMHNqSur0u9l67JNeO5H
r11T0xHE2WSYlMyWcjvEDX9BT/aGQnV+FPAh56loLNFdRIbOXKexH26Wkleyaaot
14Z1+PZPkw/FeLo=
-----END CERTIFICATE-----
4 changes: 3 additions & 1 deletion packages/vite/package.json
Expand Up @@ -14,7 +14,8 @@
"dist",
"client.d.ts",
"src/client",
"types"
"types",
"dev-cert.pem"
],
"engines": {
"node": ">=12.2.0"
Expand All @@ -36,6 +37,7 @@
"build-temp-types": "tsc --emitDeclarationOnly --outDir temp/node -p src/node",
"ci-build": "rimraf dist && run-s build-bundle build-types",
"patch-types": "node scripts/patchTypes.cjs",
"generate-selfsigned": "node scripts/generateSelfsignedCertificate.cjs",
"roll-types": "api-extractor run && rimraf temp",
"lint": "eslint --ext .ts src/**",
"format": "prettier --write --parser typescript \"src/**/*.ts\"",
Expand Down
79 changes: 79 additions & 0 deletions packages/vite/scripts/generateSelfsignedCertificate.cjs
@@ -0,0 +1,79 @@
const fs = require('fs')
const path = require('path')
const { generate } = require('selfsigned')

/**
* https://github.com/webpack/webpack-dev-server/blob/master/lib/utils/createCertificate.js
*
* Copyright JS Foundation and other contributors
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/webpack/webpack-dev-server/blob/master/LICENSE
*/
function createCertificate() {
const pems = generate(null, {
algorithm: 'sha256',
days: 3650, // 10 years should be enough for development-only insecure certificate
keySize: 2048,
extensions: [
// {
// name: 'basicConstraints',
// cA: true,
// },
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
timeStamping: true
},
{
name: 'subjectAltName',
altNames: [
{
// type 2 is DNS
type: 2,
value: 'localhost'
},
{
type: 2,
value: 'localhost.localdomain'
},
{
type: 2,
value: 'lvh.me'
},
{
type: 2,
value: '*.lvh.me'
},
{
type: 2,
value: '[::1]'
},
{
// type 7 is IP
type: 7,
ip: '127.0.0.1'
},
{
type: 7,
ip: 'fe80::1'
}
]
}
]
})
return pems.private + pems.cert
}

const cert = createCertificate()
fs.writeFileSync(path.join(__dirname, '..', 'dev-cert.pem'), cert)
110 changes: 8 additions & 102 deletions packages/vite/src/node/http.ts
@@ -1,4 +1,4 @@
import fs, { promises as fsp } from 'fs'
import fs from 'fs'
import path from 'path'
import { Server as HttpServer } from 'http'
import { ServerOptions as HttpsServerOptions } from 'https'
Expand Down Expand Up @@ -106,8 +106,7 @@ export async function resolveHttpServer(
}

export async function resolveHttpsConfig(
https?: boolean | HttpsServerOptions,
cacheDir?: string
https?: boolean | HttpsServerOptions
): Promise<HttpsServerOptions | undefined> {
if (!https) return undefined

Expand All @@ -121,7 +120,7 @@ export async function resolveHttpsConfig(
pfx: readFileIfExists(pfx)
})
if (!httpsOption.key || !httpsOption.cert) {
httpsOption.cert = httpsOption.key = await getCertificate(cacheDir)
httpsOption.cert = httpsOption.key = getCertificate()
}
return httpsOption
}
Expand All @@ -137,104 +136,11 @@ function readFileIfExists(value?: string | Buffer | any[]) {
return value
}

/**
* https://github.com/webpack/webpack-dev-server/blob/master/lib/utils/createCertificate.js
*
* Copyright JS Foundation and other contributors
* This source code is licensed under the MIT license found in the
* LICENSE file at
* https://github.com/webpack/webpack-dev-server/blob/master/LICENSE
*/
async function createCertificate() {
const { generate } = await import('selfsigned')
const pems = generate(null, {
algorithm: 'sha256',
days: 30,
keySize: 2048,
extensions: [
// {
// name: 'basicConstraints',
// cA: true,
// },
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
timeStamping: true
},
{
name: 'subjectAltName',
altNames: [
{
// type 2 is DNS
type: 2,
value: 'localhost'
},
{
type: 2,
value: 'localhost.localdomain'
},
{
type: 2,
value: 'lvh.me'
},
{
type: 2,
value: '*.lvh.me'
},
{
type: 2,
value: '[::1]'
},
{
// type 7 is IP
type: 7,
ip: '127.0.0.1'
},
{
type: 7,
ip: 'fe80::1'
}
]
}
]
})
return pems.private + pems.cert
}

async function getCertificate(cacheDir?: string) {
if (!cacheDir) return await createCertificate()

const cachePath = path.join(cacheDir, '_cert.pem')

try {
const [stat, content] = await Promise.all([
fsp.stat(cachePath),
fsp.readFile(cachePath, 'utf8')
])

if (Date.now() - stat.ctime.valueOf() > 30 * 24 * 60 * 60 * 1000) {
throw new Error('cache is outdated.')
}

return content
} catch {
const content = await createCertificate()
fsp
.mkdir(cacheDir, { recursive: true })
.then(() => fsp.writeFile(cachePath, content))
.catch(() => {})
return content
}
function getCertificate() {
return fs.readFileSync(
path.join(__dirname, '..', '..', '..', 'dev-cert.pem'),
'utf-8'
)
}

export async function httpServerStart(
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/preview.ts
Expand Up @@ -70,7 +70,7 @@ export async function preview(
const httpServer = await resolveHttpServer(
config.preview,
app,
await resolveHttpsConfig(config.preview?.https, config.cacheDir)
await resolveHttpsConfig(config.preview?.https)
)

// cors
Expand Down
8 changes: 0 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5c727ac

Please sign in to comment.