Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/factorhouse/slipway
A Clojure Companion to Jetty
https://github.com/factorhouse/slipway
clojure https jaas jetty ldap ring sente web-server websockets
Last synced: 2 days ago
JSON representation
A Clojure Companion to Jetty
- Host: GitHub
- URL: https://github.com/factorhouse/slipway
- Owner: factorhouse
- License: apache-2.0
- Created: 2022-04-06T01:03:15.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-10-07T08:48:35.000Z (about 1 month ago)
- Last Synced: 2024-11-09T21:38:17.610Z (9 days ago)
- Topics: clojure, https, jaas, jetty, ldap, ring, sente, web-server, websockets
- Language: Clojure
- Homepage:
- Size: 1.1 MB
- Stars: 67
- Watchers: 4
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Slipway: a Clojure Companion to Jetty
[![Slipway Test](https://github.com/operatr-io/slipway/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/operatr-io/slipway/actions/workflows/ci.yml)
### Versions
| Jetty Version | Current Jetty Dependency | Clojars Project |
| ------------- | ------------------------ | --------------- |
| Jetty 9 | 9.4.56.v20240826 | [![Clojars Project](https://img.shields.io/clojars/v/io.factorhouse/slipway-jetty9.svg)](https://clojars.org/io.factorhouse/slipway-jetty9) |
| Jetty 10 | 10.0.24 | [![Clojars Project](https://img.shields.io/clojars/v/io.factorhouse/slipway-jetty10.svg)](https://clojars.org/io.factorhouse/slipway-jetty10) |
| Jetty 11 | 11.0.24 | [![Clojars Project](https://img.shields.io/clojars/v/io.factorhouse/slipway-jetty11.svg)](https://clojars.org/io.factorhouse/slipway-jetty11) |
| Jetty 12 | - | Available once Jetty 12 stabilises. |----
## Slipway by [Factor House](https://factorhouse.io)
* [Introduction](#introduction)
* [Prior Art](#prior-art)
* [Why Jetty?](#why-jetty)
* [Why Slipway?](#why-slipway)
* [Requirements](#requirements)
* [Primary Goals](#primary-goals)
* [Secondary Goals](#secondary-goals)
* [Future Goals](#future-goals)
* [Out of Scope](#out-of-scope)
* [Non-Goals](#non-goals)
* [Which Version to Use?](#which-version-to-use)
* [Using Slipway](#using-slipway)
* [Quick Start](#quick-start)
* [Example Servers](#example-servers)
* [Configuring Slipway](#configuring-slipway)
* [:slipway](#slipway)
* [:slipway.server](#slipwayserver)
* [:slipway.handler](#slipwayhandler)
* [:slipway.websockets](#slipwaywebsockets)
* [:slipway.session](#slipwaysession)
* [:slipway.security](#slipwaysecurity)
* [:slipway.connector.http](#slipwayconnectorhttp)
* [:slipway.connector.https](#slipwayconnectorhttps)
* [:slipway.handler.gzip](#slipwayhandlergzip)
* [Sente Websockets](#sente-websockets)
* [JAAS Authentication](#jaas-authentication)
* [-Djava.security.auth.login.config](#-djavasecurityauthloginconfig)
* [Hash Authentication](#hash-authentication)
* [LDAP Authentication](#ldap-authentication)
* [License](#license)
* [Source Notes](#source-notes)
* [Contributing](#contributing)
----![Slipway Login](docs/img/slipway-auth.png)
# Introduction
[Eclipse Jetty](https://www.eclipse.org/jetty/) is the web server at the heart of our product, [Kpow for Apache Kafka®](https://factorhouse.io/kpow).
Slipway is a [Clojure](https://clojure.org/) companion to embedded Jetty with Websocket support.
Slipway configuration models Jetty instead of exposing a simplified DSL. This approach allows leverage of all Jetty capabilities while providing sensible defaults for basic behaviour. If in doubt, read the [Jetty docs](https://www.eclipse.org/jetty/documentation/).
Use the [Community Edition](https://kpow.io/get-started/) of Kpow with our [local-repo](https://github.com/factorhouse/kpow-local) to see Slipway in action.
## Prior Art
Slipway is based on, and in some cases includes code from the following projects:
* [sunng87/ring-jetty9-adapter](https://github.com/sunng87/ring-jetty9-adapter) by [Ning Sun](https://github.com/sunng87)
* [ring-clojure/ring](https://github.com/ring-clojure/ring/tree/master/ring-jetty-adapter) by [James Reeves](https://github.com/weavejester)We appreciate the open-source work of Ning Sun and James that forms the base of this project.
## Why Jetty?
Jetty is a mature, stable, commercially supported project with an [active, experienced](https://github.com/eclipse/jetty.project/graphs/contributors) team of core contributors.
Ubiquitous in the enterprise Java world, Jetty has many eyes raising issues and driving improvement.
Jetty is a great choice of web-server for a general purpose web-application.
## Why Slipway?
### Requirements
Kpow is a web-application with a SPA UI served by websockets.
We have a hard requirement to support customers on Java 8 and Java 11+.
Kpow has seemingly every possible Jetty configuration option in use by at least one end-user.
We incorporate automated NVD scanning and feedback from external security teams.
### Primary Goals
Slipway provides first-class, extensible support for:
* Core Jetty capabilities in Jetty-style
* HTTP 1.1
* HTTPS / SSL
* Synchronous handlers
* JAAS Authentication (LDAP, HashUser, etc)
* Form / basic authentication
* WebSockets
* Java 8 / 11+
* Jetty 9 / 10 / 11
* Session management
* Proxy protocol / http forwarded
* Common / sensible defaults (e.g. gzip compression)
* Configurable error handling
* Automated CVE scanning with NVD
* Comprehensive integration tests
* Ring compatibility### Secondary Goals
* Broad support for general Jetty use-cases / configuration
### Future Goals
* Backport our SAML, OpenID and OAuth authentication implementations
* Open-source a full-stack example application using slipway in [Shortcut](https://github.com/factorhouse/shortcut).### Out of Scope
* Http2/3
* Asynchronous Handlers
* Ajax (including auto-fallback)### Non-Goals
* A simplified DSL for Jetty
### Which Version to Use?
* Jetty 9: If you require running with Java 8
* Jetty 10: Recommended for general use, requires Java 11+
* Jetty 11: If you want to run with Jakarta rather than Javax, requires Java 11+## Using Slipway
### Quick Start
Choose a project by Jetty version, then open a REPL.
Start slipway with a ring-handler and a map of configuration options:
```clojure
(require '[slipway :as slipway])
(require '[slipway.server :as server])
(require '[slipway.connector.http :as http])(defn handler [_] {:status 200 :body "Hello world"})
(def http-connector #::http{:port 3000})
(slipway/start handler #::server{:connectors [http-connector]})
```Your hello world application is now running on [http://localhost:3000](http://localhost:3000).
### Example Servers
Various configurations of Slipway can be found in the [example.clj](common/test/slipway/example.clj) namespace.
The stateful start!/stop! functions within that namespace are not considered canonical for Slipway, they are a convenience for our integration tests.
```clojure
(require '[slipway.example :as example])(example/start! [:http :hash-auth])
```Your sample application with [property file based auth](https://docs.kpow.io/authentication/file/) is now available on [http://localhost:3000](http://localhost:3000).
Login with jetty/jetty, admin/admin, plain/plain, other/other, or user/password as defined in [hash-realm.properties](common/dev-resources/jaas/hash-realm.properties).
After login the default home-page presents some useful links for user info and error pages.
-----
![Slipway Home](docs/img/slipway-home.png)
## Configuring Slipway
Jetty is sophisticated as it addresses a complex domain with flexibility and configurability.
Slipway holds close to Jetty idioms for configuration rather than presenting a simplified DSL.
Slipway takes a map of namespaced configuration.
### :slipway
The top-level namespace provides configuration to determine if slipway joins the Jetty threadpool.
```clojure
#:slipway{:join? "join the Jetty threadpool, blocks the calling thread until jetty exits, default false"}
```### :slipway.server
Configuration of core server options.
```clojure
#:slipway.server{:handler "the base Jetty handler implementation (:default defmethod impl found in slipway.handler)"
:connectors "the connectors supported by this server"
:thread-pool "the thread-pool used by this server (leave null for reasonable defaults)"
:error-handler "the error-handler used by this server for Jetty level errors"}
```#### :slipway.server/handler
Slipway provides the following default server-handler implementations:
1. [slipway-jetty9/src/slipway/handler.clj](slipway-jetty9/src/slipway/handler.clj#L61): for Jetty 9
2. [common-jetty1x/src/slipway/handler.clj](common-jetty1x/src/slipway/handler.clj#L45): for Jetty 10/11Use a custom server-handler by implementing a new server/handler defmethod and configuring the dispatch key.
#### :slipway.server/connectors
Slipway accepts a list of server-connectors, allowing you to run multi-connector setups, e.g.
```clojure
(ns slipway.example
(:require [slipway.connector.http :as http]
[slipway.connector.https :as https]
[slipway.server :as server]))(def http-connector #::http{:port 3000})
(def https-connector #::https{:port 3443
:keystore "dev-resources/my-keystore.jks"
:keystore-type "PKCS12"
:keystore-password "password"
:truststore "dev-resources/my-truststore.jks"
:truststore-password "password"
:truststore-type "PKCS12"})(def server #::server{:connectors [http-connector https-connector]})
```#### :slipway.server/thread-pool
Leave nil for the sensible default threadpool or provide a concrete `org.eclipse.jetty.util.thread.ThreadPool`.
#### :slipway.server/error-handler
Provide a concrete `org.eclipse.jetty.server.handler.ErrorHandler` to manage Jetty-level errors (not to be confused with ring / application level errors which will be handled separately within your application).
Slipway provides a utility namespace for general-purpose ErrorHandler creation and error logging:
1. [common-javax/src/slipway/error.clj](common-javax/src/slipway/error.clj#L14): Jetty 9 and 10.
2. [common-jakarta/src/slipway/error.clj](common-jakarta/src/slipway/error.clj#L14): Jetty 11.### :slipway.handler
Configuration of the default server-handler (same for all versions of Jetty).
```clojure
#:slipway.handler{:context-path "the root context path, default '/'"
:ws-path "the path serving the websocket upgrade handler, default '/chsk'"
:null-path-info? "true if /path is not redirected to /path/, default true"}
```### :slipway.websockets
Configuration of websockets options.
Jetty 10/11 provides more configurability of websockets, as you can see in the different options below:
```clojure
;; Jetty 10 / Jetty 11 Websockets
#:slipway.websockets{:idle-timeout "max websocket idle time (in ms), default 500000"
:input-buffer-size "max websocket input buffer size (in bytes)"
:output-buffer-size "max websocket output buffer size (in bytes)"
:max-text-message-size "max websocket text message size (in bytes, default 65536)"
:max-binary-message-size "max websocket binary message size (in bytes)"
:max-frame-size "max websocket frame size (in bytes)"
:auto-fragment "websocket auto fragment (boolean)"};; Jetty 9 Websockets
#:slipway.websockets{:idle-timeout "max websocket idle time (in ms), default 500000"
:input-buffer-size "max websocket input buffer size"
:max-text-message-size "max websocket text message size"
:max-binary-message-size "max websocket binary message size"}
```See the Jetty docs to understand how to tune websockets for your own purposes.
### :slipway.session
Configuration of http session options.
```clojure
#:slipway.session{:secure-request-only? "set the secure flag on session cookies (default true)"
:http-only? "set the http-only flag on session cookies (default true)"
:same-site "set session cookie same-site policy to :none, :lax, or :strict (default :strict)"
:max-inactive-interval "max session idle time (in s, default -1)"
:tracking-modes "a set (colloection) of #{:cookie, :ssl, or :url}"
:cookie-name "the name of the session cookie"
:session-id-manager "the meta manager used for cross context session management"
:refresh-cookie-age "max time before a session cookie is re-set (in s)"
:path-parameter-name "name of path parameter used for URL session tracking"}
```### :slipway.security
Configuration of Jetty auth options.
See examples below for configuration guides to JAAS and HASH authentication.
```clojure
#:slipway.security{:realm "the Jetty authentication realm"
:hash-user-file "the path to a Jetty Hash User File"
:login-service "a Jetty LoginService identifier, 'jaas' and 'hash' supported by default"
:identity-service "a concrete Jetty IdentityService"
:authenticator "a concrete Jetty Authenticator (e.g. FormAuthenticator or BasicAuthenticator)"
```### :slipway.connector.http
Configuration of an HTTP server connector.
```clojure
#: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."
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"
:idle-timeout "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
:proxy-protocol? "if true, add the ProxyConnectionFactory. See Jetty Proxy Protocol docs"
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
:configurator "a fn taking the final connector as argument, allowing further configuration"}
````### :slipway.connector.https
Configuration of an HTTPS server connector.
```clojure
#:slipway.connector.https{: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"
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 443"
:idle-timeout "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
:proxy-protocol? "if true, add the ProxyConnectionFactor. See Jetty Proxy Protocol docs"
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
:configurator "a fn taking the final connector as argument, allowing further configuration"
:keystore "keystore to use, either path (String) or concrete KeyStore"
:keystore-type "type of keystore, e.g. JKS"
:keystore-password "password of the keystore"
:key-manager-password "password for the specific key within the keystore"
:truststore "truststore to use, either path (String) or concrete KeyStore"
:truststore-password "password of the truststore"
:truststore-type "type of the truststore, eg. JKS"
:include-protocols "a list of protocol name patterns to include in SSLEngine"
:exclude-protocols "a list of protocol name patterns to exclude from SSLEngine"
:replace-exclude-protocols? "if true will replace existing exclude-protocols, otherwise will add them"
:exclude-ciphers "a list of cipher suite names to exclude from SSLEngine"
:replace-exclude-ciphers? "if true will replace existing exclude-ciphers, otherwise will add them"
:security-provider "the security provider name"
:client-auth "either :need or :want to set the corresponding need/wantClientAuth field"
:ssl-context "a concrete pre-configured SslContext"
:sni-required? "true if a SNI certificate is required, default false"
:sni-host-check? "true if the SNI Host name must match, default false"}
```### :slipway.handler.gzip
Configuration of the Gzip Handler.
```clojure
#:slipway.handler.gzip{:enabled? "is gzip enabled? default true"
:included-mime-types "mime types to include (without charset or other parameters), leave nil for default types"
:excluded-mime-types "mime types to exclude (replacing any previous exclusion set)"
:min-gzip-size "min response size to trigger dynamic compression (in bytes, default 1024)"}
```### Sente Websockets
Slipway supports [Sente](https://github.com/ptaoussanis/sente) out-of-the box.
The entry-point can be found in the [slipway/sente.clj](/common/src/slipway/sente.clj#L66) namespace.
```clojure
(defn start-server
[opts]
(let [server (sente/make-channel-socket-server! (slipway.sente/get-sch-adapter) opts)
{:keys [ch-recv send-fn connected-uids ajax-get-or-ws-handshake-fn]} server]
{:ws-handshake ajax-get-or-ws-handshake-fn
:ch-chsk ch-recv
:chsk-send! (partial send-msg connected-uids send-fn)
:connected-uids connected-uids}))
```Refer to Sente's [getting started guide](https://github.com/ptaoussanis/sente#getting-started) for more information.
### JAAS Authentication
JAAS implements a Java version of the standard Pluggable Authentication Module (PAM) framework.
JAAS can be used for two purposes:
* for authentication of users, to reliably and securely determine who is currently executing Java code
* for authorization of users to ensure they have the access control rights (permissions) required to do the actions performed.For more information visit the [Jetty documentation](https://www.eclipse.org/jetty/documentation/jetty-10/operations-guide/index.html#og-jaas).
Various configurations of Slipway with JAAS auth can be found in the example.clj namespace.
#### -Djava.security.auth.login.config
Start your application (JAR or REPL session) with the additional JVM option
`-Djava.security.auth.login.config=/some/path/to/jaas.config`
For example configurations refer to [this tutorial](https://wiki.eclipse.org/Jetty/Tutorial/JAAS#Configuring_a_JAASLoginService).
#### Hash Authentication
The simplest JAAS authentication module. A static list of hashed users in a file.
Example `jaas.config`: ('my-realm' must be the same as the configured :realm)
```
my-realm {
org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required
debug="true"
file="dev-resources/jaas/hash-realm.properties";
};
```Example `hash-realm.properties`:
```
# This file defines users passwords and roles for a HashUserRealm
#
# The format is
# : [, ...]
#
# Passwords may be clear text, obfuscated or checksummed. The class
# org.eclipse.jetty.util.security.Password should be used to generate obfuscated
# passwords or password checksums
#
# If DIGEST Authentication is used, the password must be in a recoverable
# format, either plain text or OBF:.
#
jetty: MD5:164c88b302622e17050af52c89945d44,kafka-users,content-administrators
admin: CRYPT:adpexzg3FUZAk,server-administrators,content-administrators,kafka-admins
other: OBF:1xmk1w261u9r1w1c1xmq,kafka-admins,kafka-users
plain: plain,content-administrators
user: password,kafka-users
# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password
digest: MD5:6e120743ad67abfbc385bc2bb754e297,kafka-users
```#### LDAP Authentication
Example `jaas.config`: ('my-realm' must be the same as the configured :realm)
```
my-realm {
org.eclipse.jetty.jaas.spi.LdapLoginModule required
useLdaps="false"
debug="true"
contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
hostname="localhost"
port="10389"
bindDn="uid=admin,ou=system"
bindPassword="AES:ARClD4Hz3A2VpdCGqZArl/OglnIawMHRzW0cVjraODxIeg=="
authenticationMethod="simple"
forceBindingLogin="true"
userBaseDn="OU=Users,DC=example,DC=com"
userRdnAttribute="uid"
userIdAttribute="uid"
userPasswordAttribute="userPassword"
roleBaseDn="OU=Groups,DC=example,DC=com"
roleNameAttribute="roleName"
roleMemberAttribute="uniqueMember"
roleObjectClass="groupOfUniqueNames";
};
```### Source Notes
1. The slipway.websockets namespace is an adaption of Ning Sun's Websockets implementation.
2. The slipway.servlet namespace includes some functions from James Reeves ring project.
3. The example application uses Tailwind UI CSS, this is permitted under the terms of our license.## Contributing
We are very welcoming of any bug tickets and/or minor fixes, but we do not currently welcome larger functional contributions.
Slipway is at the heart of our commercial software and as such we take a conservative approach to modelling Jetty's capabilities.
## License
Distributed under the Apache 2.0 License.
Copyright (c) [Factor House](https://factorhouse.io)