Skip to content

Commit 65a7280

Browse files
authored
Merge pull request #23 from factorhouse/hsts-header-support
HSTS Header Support
2 parents 89eb979 + 949fa8a commit 65a7280

File tree

17 files changed

+232
-81
lines changed

17 files changed

+232
-81
lines changed

.github/workflows/ci.yml

+18-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ name: Slipway Test
22

33
on: [push]
44

5+
env: # runner has 7g of ram
6+
JVM_OPTS: -Xmx6G
7+
58
jobs:
69

7-
clojure:
10+
build:
11+
812
runs-on: ubuntu-latest
913

1014
strategy:
@@ -31,7 +35,7 @@ jobs:
3135
java-version: '11'
3236

3337
- name: Install clojure tools
34-
uses: DeLaGuardo/setup-clojure@12.3
38+
uses: DeLaGuardo/setup-clojure@13.0
3539
with:
3640
lein: 'latest'
3741
github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -57,13 +61,22 @@ jobs:
5761
run: lein uberjar
5862

5963
- name: NVD
60-
working-directory: ./${{ matrix.project }}
61-
run: ../scripts/dependency-checker.sh
64+
uses: dependency-check/Dependency-Check_Action@main
65+
env:
66+
# actions/setup-java changes JAVA_HOME so it needs to be reset to match the depcheck image
67+
JAVA_HOME: /opt/jdk
68+
with:
69+
project: ${{ matrix.project }}
70+
path: ${{ matrix.project }}/target
71+
format: 'HTML'
72+
out: ${{ matrix.project }}/reports
73+
args: >
74+
--suppression ${{ matrix.project }}/dependency-check-suppressions.xml
6275
6376
- name: Persist NVD
6477
if: always()
6578
uses: actions/upload-artifact@v4
6679
with:
6780
name: nvd-${{ matrix.project }}-${{ github.sha }}
68-
path: ./${{ matrix.project }}/dependency-check/report/*
81+
path: ${{ matrix.project }}/reports/*
6982
retention-days: 1

CHANGELOG.md

+14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
# Change Log
22
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/)
33

4+
## [1.1.18] - 2024-12-09
5+
6+
Introduce ability configure HSTS (HTTP Strict Transport Security) with new slipway.connector.https settings:
7+
8+
* :sts-max-age
9+
* :sts-include-subdomains?
10+
11+
For more informmation, see: https://github.com/factorhouse/kpow/issues/35
12+
13+
Also made these http/https settings configurable (default false, previously hard-coded to false):
14+
15+
* :send-server-version?
16+
* :send-date-header?
17+
418
## [1.1.17] - 2024-09-05
519

620
Bump to latest Jetty version (11.0.24 or equivalent)

README.md

+21-15
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ Jetty is sophisticated as it addresses a complex domain with flexibility and con
186186

187187
Slipway holds close to Jetty idioms for configuration rather than presenting a simplified DSL.
188188

189-
Slipway takes a map of namespaced configuration.
189+
Slipway takes a single map of namespaced configuration. Namespaces correspond to Jetty domain models, and can be considered as separate maps and then merged.
190190

191191
### :slipway
192192

@@ -310,25 +310,27 @@ Configuration of Jetty auth options.
310310
See examples below for configuration guides to JAAS and HASH authentication.
311311

312312
```clojure
313-
#:slipway.security{:realm "the Jetty authentication realm"
314-
:hash-user-file "the path to a Jetty Hash User File"
315-
:login-service "a Jetty LoginService identifier, 'jaas' and 'hash' supported by default"
316-
:identity-service "a concrete Jetty IdentityService"
317-
:authenticator "a concrete Jetty Authenticator (e.g. FormAuthenticator or BasicAuthenticator)"
313+
#:slipway.security{:realm "the Jetty authentication realm"
314+
:hash-user-file "the path to a Jetty Hash User File"
315+
:login-service "a Jetty LoginService identifier, 'jaas' and 'hash' supported by default"
316+
:identity-service "a concrete Jetty IdentityService"
317+
:authenticator "a concrete Jetty Authenticator (e.g. FormAuthenticator or BasicAuthenticator)"
318318
```
319319

320320
### :slipway.connector.http
321321

322322
Configuration of an HTTP server connector.
323323

324324
```clojure
325-
#:slipway.connector.http{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces."
326-
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"
327-
:idle-timeout "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
328-
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
329-
:proxy-protocol? "if true, add the ProxyConnectionFactory. See Jetty Proxy Protocol docs"
330-
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
331-
:configurator "a fn taking the final connector as argument, allowing further configuration"}
325+
#:slipway.connector.http{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces."
326+
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"
327+
:idle-timeout "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
328+
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
329+
:proxy-protocol? "if true, add the ProxyConnectionFactory. See Jetty Proxy Protocol docs"
330+
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
331+
:configurator "a fn taking the final connector as argument, allowing further configuration"
332+
:send-server-version? "if true, send the Server header in responses"
333+
:send-date-header? "if true, send the Date header in responses"}
332334
````
333335

334336
### :slipway.connector.https
@@ -358,8 +360,12 @@ Configuration of an HTTPS server connector.
358360
:security-provider "the security provider name"
359361
:client-auth "either :need or :want to set the corresponding need/wantClientAuth field"
360362
:ssl-context "a concrete pre-configured SslContext"
361-
:sni-required? "true if a SNI certificate is required, default false"
362-
:sni-host-check? "true if the SNI Host name must match, default false"}
363+
:sni-required? "if true SNI is required, else requests will be rejected with 400 response, default false"
364+
:sni-host-check? "if true the SNI Host name must match when there is an SNI certificate, default false"
365+
:sts-max-age "set the Strict-Transport-Security max age in seconds, default -1"
366+
:sts-include-subdomains? "true if a include subdomain property is sent with any Strict-Transport-Security header"
367+
:send-server-version? "if true, send the Server header in responses"
368+
:send-date-header? "if true, send the Date header in responses"}
363369
```
364370

365371
### :slipway.handler.gzip

common-jetty1x/src/slipway/websockets.clj

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
(extend-protocol common.ws/WebSocketSend
3131

32+
#_:clj-kondo/ignore
3233
(Class/forName "[B")
3334
(-send!
3435
([ba ws]
@@ -67,6 +68,7 @@
6768

6869
(extend-protocol common.ws/WebSocketPing
6970

71+
#_:clj-kondo/ignore
7072
(Class/forName "[B")
7173
(-ping! [ba ws] (common.ws/-ping! (ByteBuffer/wrap ba) ws)))
7274

common/src/slipway.clj

+4-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@
3636
:security-provider "the security provider name"
3737
:client-auth "either :need or :want to set the corresponding need/wantClientAuth field"
3838
:ssl-context "a concrete pre-configured SslContext"
39-
:sni-required? "true if a SNI certificate is required, default false"
40-
:sni-host-check? "true if the SNI Host name must match, default false"}
39+
:sni-required? "true if SNI is required, else requests will be rejected with 400 response, default false"
40+
:sni-host-check? "true if the SNI Host name must match when there is an SNI certificate, default false"
41+
:sts-max-age "set the Strict-Transport-Security max age in seconds, default -1"
42+
:sts-include-subdomains? "true if a include subdomain property is sent with any Strict-Transport-Security header"}
4143

4244
#:slipway.connector.http{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces."
4345
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"

common/src/slipway/connector/http.clj

+14-10
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,25 @@
55
HttpConnectionFactory ProxyConnectionFactory Server ServerConnector)))
66

77
(defn default-config ^HttpConfiguration
8-
[{::keys [http-forwarded?]}]
8+
[{::keys [http-forwarded? send-server-version? send-date-header?]
9+
:or {send-server-version? false
10+
send-date-header? false}}]
911
(let [config (doto (HttpConfiguration.)
10-
(.setSendServerVersion false)
11-
(.setSendDateHeader false))]
12+
(.setSendServerVersion send-server-version?)
13+
(.setSendDateHeader send-date-header?))]
1214
(when http-forwarded? (.addCustomizer config (ForwardedRequestCustomizer.)))
1315
config))
1416

1517
(comment
16-
#:slipway.connector.http{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
17-
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"
18-
:idle-timeout "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
19-
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
20-
:proxy-protocol? "if true, add the ProxyConnectionFactor. See Jetty Proxy Protocol docs"
21-
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
22-
:configurator "a fn taking the final connector as argument, allowing further configuration"})
18+
#:slipway.connector.http{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
19+
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"
20+
:idle-timeout "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
21+
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
22+
:proxy-protocol? "if true, add the ProxyConnectionFactor. See Jetty Proxy Protocol docs"
23+
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
24+
:configurator "a fn taking the final connector as argument, allowing further configuration"
25+
:send-server-version? "if true, send the Server header in responses"
26+
:send-date-header? "if true, send the Date header in responses"})
2327

2428
(defmethod server/connector ::connector
2529
[^Server server {::keys [host port idle-timeout proxy-protocol? http-forwarded? configurator http-config]

common/src/slipway/connector/https.clj

+21-7
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,25 @@
88
(org.eclipse.jetty.util.ssl SslContextFactory$Server)))
99

1010
(defn default-config ^HttpConfiguration
11-
[{::keys [port http-forwarded? sni-required? sni-host-check?] :or {sni-required? false sni-host-check? false}}]
12-
(log/infof "sni required? %s, sni host check? %s" sni-required? sni-host-check?)
11+
[{::keys [port http-forwarded? sni-required? sni-host-check? sts-max-age sts-include-subdomains? send-server-version?
12+
send-date-header?]
13+
:or {sni-required? false
14+
sni-host-check? false
15+
sts-max-age -1
16+
sts-include-subdomains? false
17+
send-server-version? false
18+
send-date-header? false}}]
19+
(log/infof "sni required? %s, sni host check? %s, sts-max-age %s, sts-include-subdomains? %s"
20+
sni-required? sni-host-check? sts-max-age sts-include-subdomains?)
1321
(let [config (doto (HttpConfiguration.)
1422
(.setSecurePort port)
15-
(.setSendServerVersion false)
16-
(.setSendDateHeader false)
23+
(.setSendServerVersion send-server-version?)
24+
(.setSendDateHeader send-date-header?)
1725
(.addCustomizer (doto (SecureRequestCustomizer.)
1826
(.setSniRequired sni-required?)
19-
(.setSniHostCheck sni-host-check?))))]
27+
(.setSniHostCheck sni-host-check?)
28+
(.setStsMaxAge sts-max-age)
29+
(.setStsIncludeSubDomains sts-include-subdomains?))))]
2030
(when http-forwarded? (.addCustomizer config (ForwardedRequestCustomizer.)))
2131
config))
2232

@@ -98,8 +108,12 @@
98108
:security-provider "the security provider name"
99109
:client-auth "either :need or :want to set the corresponding need/wantClientAuth field"
100110
:ssl-context "a concrete pre-configured SslContext"
101-
:sni-required? "true if a SNI certificate is required, default false"
102-
:sni-host-check? "true if the SNI Host name must match, default false"})
111+
:sni-required? "if true SNI is required, else requests will be rejected with 400 response, default false"
112+
:sni-host-check? "if true the SNI Host name must match when there is an SNI certificate, default false"
113+
:sts-max-age "set the Strict-Transport-Security max age in seconds, default -1"
114+
:sts-include-subdomains? "true if a include subdomain property is sent with any Strict-Transport-Security header"
115+
:send-server-version? "if true, send the Server header in responses"
116+
:send-date-header? "if true, send the Date header in responses"})
103117

104118
(defmethod server/connector ::connector
105119
[^Server server {::keys [host port idle-timeout proxy-protocol? http-config configurator]

common/src/slipway/security.clj

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@
1919
(if config
2020
(when (slurp config)
2121
(doto (JAASLoginService. realm) (.setConfiguration (Configuration/getConfiguration))))
22-
(throw (ex-info (str "start with -Djava.security.auth.login.config=/some/path/to/jaas.config to use Jetty/JAAS auth provider") {})))))
22+
(throw (ex-info "start with -Djava.security.auth.login.config=/some/path/to/jaas.config to use Jetty/JAAS auth provider" {})))))
2323

2424
(defmethod login-service "hash"
2525
[{::keys [realm hash-user-file]}]
2626
(log/infof "initializing HashLoginService - realm: %s, realm file: %s" realm hash-user-file)
2727
(if hash-user-file
2828
(when (slurp hash-user-file)
2929
(HashLoginService. realm hash-user-file))
30-
(throw (ex-info (str "set the path to your hash user realm properties file") {}))))
30+
(throw (ex-info "set the path to your hash user realm properties file" {}))))
3131

3232
(defn user
3333
[^Request base-request]

common/test/slipway/example.clj

+17
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
:truststore-password "password"
3030
:truststore-type "PKCS12"})
3131

32+
(def hsts #::https{:sts-max-age 31536000
33+
:sts-include-subdomains? true})
34+
35+
(def hsts-no-subdomains #::https{:sts-max-age 31536000})
36+
37+
(def hsts-no-max-age #::https{:sts-include-subdomains? true})
38+
3239
(def form-authenticator (FormAuthenticator. "/login" "/login-retry" false))
3340

3441
(def options
@@ -38,6 +45,16 @@
3845
:https #::server{:connectors [https-connector]
3946
:error-handler app/server-error-handler}
4047

48+
:hsts #::server{:connectors [(merge https-connector hsts)]
49+
:error-handler app/server-error-handler}
50+
51+
:hsts-no-subdomains #::server{:connectors [(merge https-connector hsts-no-subdomains)]
52+
:error-handler app/server-error-handler}
53+
54+
;; this is an error condition / incorrect configuration - subdomains requires max-age set
55+
:hsts-no-max-age #::server{:connectors [(merge https-connector hsts-no-max-age)]
56+
:error-handler app/server-error-handler}
57+
4158
:http+https #::server{:connectors [http-connector https-connector]
4259
:error-handler app/server-error-handler}
4360

0 commit comments

Comments
 (0)