Skip to content

Commit 875408d

Browse files
authored
Merge pull request #18 from tayloraswift/https-everywhere
HTTPS everywhere
2 parents 746f96c + 676b058 commit 875408d

30 files changed

+309
-343
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
.vscode
66

77
Assets/secrets/
8+
TestCertificates/*.pem
89
TestDeployment/data/
910

1011
node_modules/

Assets/css/Main.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Guides/GeneratingCertificates.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Generating TLS certificates
2+
3+
To simplify the implementation and facilitate testing, Unidoc uses TLS (HTTPS) everywhere, even when running locally.
4+
5+
For a seamless development experience, we recommend using [mkcert](https://github.com/FiloSottile/mkcert) to generate a local certificate authority (CA) for your development environment.
6+
7+
## Installing mkcert
8+
9+
The easiest way to install `mkcert` is to download one of its prebuilt binaries.
10+
11+
```bash
12+
$ curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
13+
chmod +x mkcert-v*-linux-amd64
14+
```
15+
16+
## Generating a local certificate authority
17+
18+
```bash
19+
$ ./mkcert-v1.4.4-linux-amd64 -install
20+
``````
21+
22+
## Generating a local certificate
23+
24+
If the `mkcert-v1.4.4-linux-amd64` binary is located in your home directory, you can generate a certificate for `localhost` by running the following from the repository root:
25+
26+
```bash
27+
$ cd TestCertificates
28+
$ ~/mkcert-v1.4.4-linux-amd64 localhost
29+
30+
```
31+
32+
Then, rename the generated files to `fullchain.pem` and `privkey.pem`.
33+
34+
```bash
35+
$ mv localhost.pem fullchain.pem
36+
$ mv localhost-key.pem privkey.pem
37+
```
38+
39+
You should now be able to run the Unidoc server locally and access it without browser warnings.
40+
41+
Keep in mind that the certificate is only valid for `localhost`; hostnames like `0.0.0.0` will still raise browser warnings.

Package.resolved

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ let package:Package = .init(
7474
.package(url: "https://github.com/tayloraswift/swift-hash", .upToNextMinor(
7575
from: "0.5.0")),
7676
.package(url: "https://github.com/tayloraswift/swift-mongodb", .upToNextMinor(
77-
from: "0.8.2")),
77+
from: "0.8.3")),
7878

7979
.package(url: "https://github.com/apple/swift-atomics", .upToNextMinor(
8080
from: "1.1.0")),
8181
.package(url: "https://github.com/apple/swift-nio", .upToNextMinor(
82-
from: "2.57.0")),
82+
from: "2.58.0")),
8383
.package(url: "https://github.com/apple/swift-nio-http2", .upToNextMinor(
8484
from: "1.27.0")),
8585
.package(url: "https://github.com/apple/swift-nio-ssl", .upToNextMinor(
@@ -177,6 +177,7 @@ let package:Package = .init(
177177
.target(name: "HTML"),
178178
.target(name: "HTTP"),
179179
.product(name: "NIOHTTP1", package: "swift-nio"),
180+
.product(name: "NIOHTTP2", package: "swift-nio-http2"),
180181
.product(name: "NIOSSL", package: "swift-nio-ssl"),
181182
.product(name: "TraceableErrors", package: "swift-grammar"),
182183
]),

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div align="center">
22

3-
<strong><em><code>unidoc</code></em></strong><br><small><code>0.2.3</code></small>
3+
<strong><em><code>unidoc</code></em></strong><br><small><code>0.2.4</code></small>
44

55
[![ci build status](https://github.com/kelvin13/swift-unidoc/actions/workflows/build.yml/badge.svg)](https://github.com/kelvin13/swift-unidoc/actions/workflows/build.yml)
66

@@ -104,6 +104,8 @@ Unidoc is tightly-integrated with Swiftinit, but you can run it locally to previ
104104

105105
Unidoc uses [MongoDB](https://www.mongodb.com/) to persist documentation. This allows for fast startup times as Unidoc performs almost no initialization, but requires you to have an active MongoDB replica set running on your local machine. See [`Testing.md`](Guides/Testing.md) for instructions on setting up a local environment.
106106

107+
Unidoc uses HTTPS everywhere. See [`GeneratingCertificates.md`](Guides/GeneratingCertificates.md) for how to generate a local and certificate and certificate authority for `localhost`.
108+
107109
TODO: Add example for running the Unidoc server locally.
108110

109111
TODO: Add example for invoking the Unidoc compiler.
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1+
import NIOSSL
2+
13
@frozen public
24
struct Localhost:ServerAuthority
35
{
4-
@inlinable internal
5-
init()
6+
public
7+
let tls:NIOSSLContext
8+
9+
@inlinable public
10+
init(tls:NIOSSLContext)
611
{
12+
self.tls = tls
713
}
814

915
@inlinable public static
10-
var scheme:ServerScheme { .http }
16+
var scheme:ServerScheme { .https }
1117
@inlinable public static
1218
var domain:String { "127.0.0.1" }
1319
}

Sources/HTTPServer/Authorities/ServerAuthority.swift

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,24 @@ import HTML
22
import NIOCore
33
import NIOHTTP1
44
import NIOPosix
5+
import NIOSSL
56
import TraceableErrors
67

78
public
8-
protocol ServerAuthority<SecurityContext>:Sendable
9+
protocol ServerAuthority:Sendable
910
{
10-
associatedtype SecurityContext = Never
11-
1211
static
1312
var scheme:ServerScheme { get }
1413
static
1514
var domain:String { get }
1615

17-
var tls:SecurityContext? { get }
16+
var tls:NIOSSLContext { get }
1817

1918
static
2019
func redact(error:any Error) -> String
2120
}
22-
23-
extension ServerAuthority where Self == Localhost
24-
{
25-
@inlinable public static
26-
var localhost:Self { .init() }
27-
}
28-
29-
extension ServerAuthority<Never>
21+
extension ServerAuthority
3022
{
31-
@inlinable public
32-
var tls:SecurityContext? { nil }
3323
/// Dumps detailed information about the caught error. This information will be shown to
3424
/// *anyone* accessing the server. In production, we strongly recommend overriding this
3525
/// default implementation to avoid inadvertently exposing sensitive data via type
@@ -66,8 +56,6 @@ extension ServerAuthority
6656
{
6757
switch self.scheme
6858
{
69-
case .http(port: 80): return "http://\(self.domain)\(uri)"
70-
case .http(port: let port): return "http://\(self.domain):\(port)\(uri)"
7159
case .https(port: 443): return "https://\(self.domain)\(uri)"
7260
case .https(port: let port): return "https://\(self.domain):\(port)\(uri)"
7361
}

Sources/HTTPServer/Authorities/ServerScheme.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@
22
enum ServerScheme
33
{
44
case https(port:Int = 443)
5-
case http(port:Int = 80)
65
}
76
extension ServerScheme
87
{
98
@inlinable public static
109
var https:Self { .https() }
11-
12-
@inlinable public static
13-
var http:Self { .http() }
1410
}
1511
extension ServerScheme
1612
{
@@ -19,7 +15,6 @@ extension ServerScheme
1915
{
2016
switch self
2117
{
22-
case .http(port: let port): return port
2318
case .https(port: let port): return port
2419
}
2520
}
@@ -28,7 +23,6 @@ extension ServerScheme
2823
{
2924
switch self
3025
{
31-
case .http: return "http"
3226
case .https: return "https"
3327
}
3428
}

Sources/HTTPServer/Channels/HTTPServerDelegate.swift

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import HTTP
22
import NIOCore
33
import NIOPosix
4-
import NIOHTTP1
4+
import NIOHTTP2
55
import NIOSSL
66

77
public
@@ -30,26 +30,14 @@ extension HTTPServerDelegate
3030
{
3131
(channel:any Channel) -> EventLoopFuture<Void> in
3232

33-
let endpoint:ServerInterfaceHandler<Authority, Self> = .init(
34-
address: channel.remoteAddress,
35-
server: self)
36-
37-
guard let tls:NIOSSLContext = authority.tls as? NIOSSLContext
38-
else
39-
{
40-
return channel.pipeline.configureHTTPServerPipeline(withErrorHandling: true)
41-
.flatMap
42-
{
43-
channel.pipeline.addHandler(endpoint)
44-
}
45-
}
46-
return channel.pipeline.addHandler(NIOSSLServerHandler.init(context: tls))
47-
.flatMap
33+
channel.pipeline.addHandler(NIOSSLServerHandler.init(context: authority.tls))
34+
.flatMap
4835
{
49-
channel.pipeline.configureHTTPServerPipeline(withErrorHandling: true)
50-
.flatMap
36+
channel.configureCommonHTTPServerPipeline
5137
{
52-
channel.pipeline.addHandler(endpoint)
38+
$0.pipeline.addHandler(ServerInterfaceHandler<Authority, Self>.init(
39+
address: channel.remoteAddress,
40+
server: self))
5341
}
5442
}
5543
}

0 commit comments

Comments
 (0)