Merge pull request #2255 from cdr/self-signed-3b2c
Fix self signed certificate for iPad
This commit is contained in:
commit
f9e0990594
|
@ -26,6 +26,7 @@ main() {
|
||||||
doctoc --title '# Install' doc/install.md > /dev/null
|
doctoc --title '# Install' doc/install.md > /dev/null
|
||||||
doctoc --title '# npm Install Requirements' doc/npm.md > /dev/null
|
doctoc --title '# npm Install Requirements' doc/npm.md > /dev/null
|
||||||
doctoc --title '# Contributing' doc/CONTRIBUTING.md > /dev/null
|
doctoc --title '# Contributing' doc/CONTRIBUTING.md > /dev/null
|
||||||
|
doctoc --title '# iPad' doc/ipad.md > /dev/null
|
||||||
|
|
||||||
if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then
|
if [[ ${CI-} && $(git ls-files --other --modified --exclude-standard) ]]; then
|
||||||
echo "Files need generation or are formatted incorrectly:"
|
echo "Files need generation or are formatted incorrectly:"
|
||||||
|
|
18
doc/FAQ.md
18
doc/FAQ.md
|
@ -3,6 +3,7 @@
|
||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
- [Questions?](#questions)
|
- [Questions?](#questions)
|
||||||
|
- [iPad Status?](#ipad-status)
|
||||||
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
|
- [How can I reuse my VS Code configuration?](#how-can-i-reuse-my-vs-code-configuration)
|
||||||
- [Differences compared to VS Code?](#differences-compared-to-vs-code)
|
- [Differences compared to VS Code?](#differences-compared-to-vs-code)
|
||||||
- [How can I request a missing extension?](#how-can-i-request-a-missing-extension)
|
- [How can I request a missing extension?](#how-can-i-request-a-missing-extension)
|
||||||
|
@ -21,7 +22,6 @@
|
||||||
- [Heartbeat File](#heartbeat-file)
|
- [Heartbeat File](#heartbeat-file)
|
||||||
- [Healthz endpoint](#healthz-endpoint)
|
- [Healthz endpoint](#healthz-endpoint)
|
||||||
- [How does the config file work?](#how-does-the-config-file-work)
|
- [How does the config file work?](#how-does-the-config-file-work)
|
||||||
- [Blank screen on iPad?](#blank-screen-on-ipad)
|
|
||||||
- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure)
|
- [Isn't an install script piped into sh insecure?](#isnt-an-install-script-piped-into-sh-insecure)
|
||||||
- [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work)
|
- [How do I make my keyboard shortcuts work?](#how-do-i-make-my-keyboard-shortcuts-work)
|
||||||
- [Differences compared to Theia?](#differences-compared-to-theia)
|
- [Differences compared to Theia?](#differences-compared-to-theia)
|
||||||
|
@ -33,6 +33,10 @@
|
||||||
|
|
||||||
Please file all questions and support requests at https://github.com/cdr/code-server/discussions.
|
Please file all questions and support requests at https://github.com/cdr/code-server/discussions.
|
||||||
|
|
||||||
|
## iPad Status?
|
||||||
|
|
||||||
|
Please see [./ipad.md](./ipad.md).
|
||||||
|
|
||||||
## How can I reuse my VS Code configuration?
|
## How can I reuse my VS Code configuration?
|
||||||
|
|
||||||
The very popular [Settings Sync](https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync) extension works.
|
The very popular [Settings Sync](https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync) extension works.
|
||||||
|
@ -144,6 +148,9 @@ For HTTPS, you can use a self signed certificate by passing in just `--cert` or
|
||||||
pass in an existing certificate by providing the path to `--cert` and the path to
|
pass in an existing certificate by providing the path to `--cert` and the path to
|
||||||
the key with `--cert-key`.
|
the key with `--cert-key`.
|
||||||
|
|
||||||
|
The self signed certificate will be generated into
|
||||||
|
`~/.local/share/code-server/self-signed.crt`.
|
||||||
|
|
||||||
If `code-server` has been passed a certificate it will also respond to HTTPS
|
If `code-server` has been passed a certificate it will also respond to HTTPS
|
||||||
requests and will redirect all HTTP requests to HTTPS.
|
requests and will redirect all HTTP requests to HTTPS.
|
||||||
|
|
||||||
|
@ -279,15 +286,6 @@ The `--config` flag or `$CODE_SERVER_CONFIG` can be used to change the config fi
|
||||||
|
|
||||||
The default location also respects `$XDG_CONFIG_HOME`.
|
The default location also respects `$XDG_CONFIG_HOME`.
|
||||||
|
|
||||||
## Blank screen on iPad?
|
|
||||||
|
|
||||||
Unfortunately at the moment self signed certificates cause a blank screen on iPadOS
|
|
||||||
|
|
||||||
There does seem to be a way to get it to work if you create your own CA and create a
|
|
||||||
certificate using the CA and then import the CA onto your iPad.
|
|
||||||
|
|
||||||
See [#1566](https://github.com/cdr/code-server/issues/1566#issuecomment-623159434).
|
|
||||||
|
|
||||||
## Isn't an install script piped into sh insecure?
|
## Isn't an install script piped into sh insecure?
|
||||||
|
|
||||||
Please give
|
Please give
|
||||||
|
|
|
@ -251,8 +251,7 @@ Visit `https://<your-domain-name>` to access `code-server`. Congratulations!
|
||||||
|
|
||||||
### Self Signed Certificate
|
### Self Signed Certificate
|
||||||
|
|
||||||
**note:** Self signed certificates do not work with iPad and will cause a blank page. You'll
|
**note:** Self signed certificates do not work with iPad normally. See [./ipad.md](./ipad.md) for details.
|
||||||
have to use [Let's Encrypt](#lets-encrypt) instead. See the [FAQ](./FAQ.md#blank-screen-on-ipad).
|
|
||||||
|
|
||||||
Recommended reading: https://security.stackexchange.com/a/8112.
|
Recommended reading: https://security.stackexchange.com/a/8112.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
|
# iPad
|
||||||
|
|
||||||
|
- [iPad](#ipad)
|
||||||
|
- [Known Issues](#known-issues)
|
||||||
|
- [How to access code-server with a self signed certificate on iPad?](#how-to-access-code-server-with-a-self-signed-certificate-on-ipad)
|
||||||
|
|
||||||
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
# iPad
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
- Getting self signed certificates certificates to work is involved, see below.
|
||||||
|
- Keyboard may disappear sometimes [#1313](https://github.com/cdr/code-server/issues/1313), [#979](https://github.com/cdr/code-server/issues/979)
|
||||||
|
- Trackpad scrolling does not work [#1455](https://github.com/cdr/code-server/issues/1455)
|
||||||
|
- See [issues tagged with the iPad label](https://github.com/cdr/code-server/issues?q=is%3Aopen+is%3Aissue+label%3AiPad) for more.
|
||||||
|
|
||||||
|
## How to access code-server with a self signed certificate on iPad?
|
||||||
|
|
||||||
|
Accessing a self signed certificate on iPad isn't as easy as accepting through all
|
||||||
|
the security warnings. Safari will prevent WebSocket connections unless the certificate
|
||||||
|
is installed as a profile on the device.
|
||||||
|
|
||||||
|
The below assumes you are using the self signed certificate that code-server
|
||||||
|
generates for you. If not, that's fine but you'll have to make sure your certificate
|
||||||
|
abides by the following guidelines from Apple: https://support.apple.com/en-us/HT210176
|
||||||
|
|
||||||
|
**note**: Another undocumented requirement we noticed is that the certificate has to have `basicConstraints=CA:true`.
|
||||||
|
|
||||||
|
The following instructions assume you have code-server installed and running
|
||||||
|
with a self signed certificate. If not, please first go through [./guide.md](./guide.md)!
|
||||||
|
|
||||||
|
**warning**: Your iPad must access code-server via a domain name. It could be local
|
||||||
|
DNS like `mymacbookpro.local` but it must be a domain name. Otherwise Safari will
|
||||||
|
refuse to allow WebSockets to connect.
|
||||||
|
|
||||||
|
1. Your certificate **must** have a subject alt name that matches the hostname
|
||||||
|
at which you will access code-server from your iPad. You can pass this to code-server
|
||||||
|
so that it generates the certificate correctly with `--cert-host`.
|
||||||
|
2. Share your self signed certificate with the iPad.
|
||||||
|
- code-server will print the location of the certificate it has generated in the logs.
|
||||||
|
|
||||||
|
```
|
||||||
|
[2020-10-30T08:55:45.139Z] info - Using generated certificate and key for HTTPS: ~/.local/share/code-server/mymbp_local.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
- You can mail it to yourself or if you have a Mac, it's easiest to just Airdrop to the iPad.
|
||||||
|
|
||||||
|
3. When opening the `*.crt` file, you'll be prompted to go into settings to install.
|
||||||
|
4. Go to `Settings -> General -> Profile`, select the profile and then hit `Install`.
|
||||||
|
- It should say the profile is verified.
|
||||||
|
5. Go to `Settings -> About -> Certificate Trust Settings` and enable full trust for
|
||||||
|
the certificate.
|
||||||
|
6. Now you can access code-server! 🍻
|
|
@ -26,6 +26,7 @@ export interface Args extends VsArgs {
|
||||||
readonly auth?: AuthType
|
readonly auth?: AuthType
|
||||||
readonly password?: string
|
readonly password?: string
|
||||||
readonly cert?: OptionalString
|
readonly cert?: OptionalString
|
||||||
|
readonly "cert-host"?: string
|
||||||
readonly "cert-key"?: string
|
readonly "cert-key"?: string
|
||||||
readonly "disable-telemetry"?: boolean
|
readonly "disable-telemetry"?: boolean
|
||||||
readonly help?: boolean
|
readonly help?: boolean
|
||||||
|
@ -101,7 +102,11 @@ const options: Options<Required<Args>> = {
|
||||||
cert: {
|
cert: {
|
||||||
type: OptionalString,
|
type: OptionalString,
|
||||||
path: true,
|
path: true,
|
||||||
description: "Path to certificate. Generated if no path is provided.",
|
description: "Path to certificate. A self signed certificate is generated if none is provided.",
|
||||||
|
},
|
||||||
|
"cert-host": {
|
||||||
|
type: "string",
|
||||||
|
description: "Hostname to use when generating a self signed certificate.",
|
||||||
},
|
},
|
||||||
"cert-key": { type: "string", path: true, description: "Path to certificate key when using non-generated cert." },
|
"cert-key": { type: "string", path: true, description: "Path to certificate key when using non-generated cert." },
|
||||||
"disable-telemetry": { type: "boolean", description: "Disable telemetry." },
|
"disable-telemetry": { type: "boolean", description: "Disable telemetry." },
|
||||||
|
|
|
@ -160,7 +160,7 @@ const main = async (args: Args, configArgs: Args): Promise<void> => {
|
||||||
proxyDomains: args["proxy-domain"],
|
proxyDomains: args["proxy-domain"],
|
||||||
socket: args.socket,
|
socket: args.socket,
|
||||||
...(args.cert && !args.cert.value
|
...(args.cert && !args.cert.value
|
||||||
? await generateCertificate()
|
? await generateCertificate(args["cert-host"] || "localhost")
|
||||||
: {
|
: {
|
||||||
cert: args.cert && args.cert.value,
|
cert: args.cert && args.cert.value,
|
||||||
certKey: args["cert-key"],
|
certKey: args["cert-key"],
|
||||||
|
@ -209,7 +209,7 @@ const main = async (args: Args, configArgs: Args): Promise<void> => {
|
||||||
logger.info(
|
logger.info(
|
||||||
args.cert && args.cert.value
|
args.cert && args.cert.value
|
||||||
? ` - Using provided certificate and key for HTTPS`
|
? ` - Using provided certificate and key for HTTPS`
|
||||||
: ` - Using generated certificate and key for HTTPS`,
|
: ` - Using generated certificate and key for HTTPS: ${humanPath(options.cert)}`,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
logger.info(" - Not serving HTTPS")
|
logger.info(" - Not serving HTTPS")
|
||||||
|
|
|
@ -54,25 +54,45 @@ export function humanPath(p?: string): string {
|
||||||
return p.replace(os.homedir(), "~")
|
return p.replace(os.homedir(), "~")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateCertificate = async (): Promise<{ cert: string; certKey: string }> => {
|
export const generateCertificate = async (hostname: string): Promise<{ cert: string; certKey: string }> => {
|
||||||
const paths = {
|
const certPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.crt`)
|
||||||
cert: path.join(tmpdir, "self-signed.cert"),
|
const certKeyPath = path.join(paths.data, `${hostname.replace(/\./g, "_")}.key`)
|
||||||
certKey: path.join(tmpdir, "self-signed.key"),
|
|
||||||
}
|
const checks = await Promise.all([fs.pathExists(certPath), fs.pathExists(certKeyPath)])
|
||||||
const checks = await Promise.all([fs.pathExists(paths.cert), fs.pathExists(paths.certKey)])
|
|
||||||
if (!checks[0] || !checks[1]) {
|
if (!checks[0] || !checks[1]) {
|
||||||
// Require on demand so openssl isn't required if you aren't going to
|
// Require on demand so openssl isn't required if you aren't going to
|
||||||
// generate certificates.
|
// generate certificates.
|
||||||
const pem = require("pem") as typeof import("pem")
|
const pem = require("pem") as typeof import("pem")
|
||||||
const certs = await new Promise<import("pem").CertificateCreationResult>((resolve, reject): void => {
|
const certs = await new Promise<import("pem").CertificateCreationResult>((resolve, reject): void => {
|
||||||
pem.createCertificate({ selfSigned: true }, (error, result) => {
|
pem.createCertificate(
|
||||||
return error ? reject(error) : resolve(result)
|
{
|
||||||
})
|
selfSigned: true,
|
||||||
|
commonName: hostname,
|
||||||
|
config: `
|
||||||
|
[req]
|
||||||
|
req_extensions = v3_req
|
||||||
|
|
||||||
|
[ v3_req ]
|
||||||
|
basicConstraints = CA:true
|
||||||
|
extendedKeyUsage = serverAuth
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
|
||||||
|
[alt_names]
|
||||||
|
DNS.1 = ${hostname}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
(error, result) => {
|
||||||
|
return error ? reject(error) : resolve(result)
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
await fs.mkdirp(tmpdir)
|
await fs.mkdirp(paths.data)
|
||||||
await Promise.all([fs.writeFile(paths.cert, certs.certificate), fs.writeFile(paths.certKey, certs.serviceKey)])
|
await Promise.all([fs.writeFile(certPath, certs.certificate), fs.writeFile(certKeyPath, certs.serviceKey)])
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
cert: certPath,
|
||||||
|
certKey: certKeyPath,
|
||||||
}
|
}
|
||||||
return paths
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generatePassword = async (length = 24): Promise<string> => {
|
export const generatePassword = async (length = 24): Promise<string> => {
|
||||||
|
|
|
@ -45,7 +45,7 @@ describe("SocketProxyProvider", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const cert = await generateCertificate()
|
const cert = await generateCertificate("localhost")
|
||||||
const options = {
|
const options = {
|
||||||
cert: fs.readFileSync(cert.cert),
|
cert: fs.readFileSync(cert.cert),
|
||||||
key: fs.readFileSync(cert.certKey),
|
key: fs.readFileSync(cert.certKey),
|
||||||
|
|
Loading…
Reference in New Issue