automatic TLS certs
This commit is contained in:
parent
f6f64b1ce8
commit
07bb76867b
2
Makefile
2
Makefile
|
@ -27,7 +27,7 @@ install: bin/geml-server
|
|||
--owner geml \
|
||||
--group geml \
|
||||
--mode 644 \
|
||||
geml.ini
|
||||
geml.example.ini
|
||||
|
||||
uninstall:
|
||||
id -u geml &>/dev/null && userdel geml
|
||||
|
|
54
README.md
54
README.md
|
@ -4,53 +4,40 @@ Gemini server written in Common Lisp
|
|||
|
||||
## /etc/geml/geml.ini
|
||||
|
||||
geml requires `cert` and `key` to be configured before it will run. And will
|
||||
have nothing to serve until you configure at least one domain and root. See
|
||||
[`geml.ini`](./geml.ini) as an example.
|
||||
geml will have nothing to serve until you configure at least one domain and
|
||||
root.
|
||||
|
||||
```
|
||||
cert = /var/lib/geml/localhost.crt
|
||||
key = /var/lib/geml/localhost.key
|
||||
|
||||
[my.gmi.capsule]
|
||||
root = /srv/gmi
|
||||
```
|
||||
|
||||
### Generate Self-Signed SSL Certificate
|
||||
|
||||
- [ ] include/write helper script for this
|
||||
|
||||
```sh
|
||||
openssl req -x509 \
|
||||
-out localhost.crt \
|
||||
-keyout localhost.key \
|
||||
-newkey rsa:2048 \
|
||||
-nodes \
|
||||
-sha256 \
|
||||
-subj '/CN=localhost' \
|
||||
-extensions EXT \
|
||||
-config <(printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Standalone Executable
|
||||
|
||||
To get an executable `bin/geml-server` run `make`.
|
||||
|
||||
```sh
|
||||
make && bin/geml-server -h
|
||||
```
|
||||
|
||||
### Systemd
|
||||
|
||||
Install the build to `/usr/local/bin/geml-server` with
|
||||
`sudo make install` which will also wire up systemd so you can `sudo systemctl
|
||||
enable --now geml`
|
||||
To install the build to `/usr/local/bin/geml-server` use `sudo make install`
|
||||
which will also wire up systemd so you can `sudo systemctl enable --now geml`
|
||||
|
||||
You'll need to `sudo systemctl restart geml` whenever you update
|
||||
`/etc/geml/geml.ini`... you did do that didn't you?
|
||||
|
||||
```sh
|
||||
sudo cp /etc/geml/geml.example.ini /etc/geml/geml.ini
|
||||
```
|
||||
|
||||
`make install` also sets up a geml system user and you may want to add yourself
|
||||
to that group.
|
||||
|
||||
```sh
|
||||
usermod -a -G geml $USER
|
||||
```
|
||||
|
||||
### yeet it
|
||||
|
||||
To undo all that run these:
|
||||
|
@ -59,12 +46,5 @@ To undo all that run these:
|
|||
sudo make uninstall
|
||||
make clean
|
||||
```
|
||||
|
||||
### SBCL
|
||||
|
||||
Start sbcl with proper readline support: `rlwrap sbcl`
|
||||
|
||||
```lisp
|
||||
(ql:quickload "geml")
|
||||
(gemini.server:start-server "/etc/geml/geml.ini")
|
||||
```
|
||||
**NOTE**: The automatically created TLS certs will still be at `/var/lib/geml`
|
||||
you can (re)use those elsewhere or also just delete them.
|
||||
|
|
4
geml.ini
4
geml.ini
|
@ -2,9 +2,7 @@
|
|||
;; see usocket: https://github.com/usocket/usocket#api-definition
|
||||
;; host = 0.0.0.0
|
||||
;; port = 1965
|
||||
|
||||
cert = /var/lib/geml/localhost.crt
|
||||
key = /var/lib/geml/localhost.key
|
||||
;; certs-dir = /var/lib/geml/
|
||||
|
||||
[localhost]
|
||||
root = /srv/gmi
|
||||
|
|
35
src/bin.lisp
35
src/bin.lisp
|
@ -24,19 +24,22 @@
|
|||
(defun bin ()
|
||||
(multiple-value-bind (options free-args)
|
||||
(handler-case
|
||||
(handler-bind ((opts:unknown-option #'unknown-option))
|
||||
(opts:get-opts))
|
||||
(opts:missing-arg (condition)
|
||||
(format t "option ~s needs an argument!~%"
|
||||
(opts:option condition)))
|
||||
(opts:arg-parser-failed (condition)
|
||||
(format t "cannot parse ~s as argument of ~s~%"
|
||||
(opts:raw-arg condition)
|
||||
(opts:option condition)))
|
||||
(opts:missing-required-option (con)
|
||||
(format t "~a~%" con)
|
||||
(opts:exit 1)))
|
||||
(cond
|
||||
((getf options :help) (opts:describe :usage-of "geml-server"))
|
||||
((getf options :version) (format t "~a~%" (asdf:component-version (asdf:find-system 'geml))))
|
||||
(t (start-server (getf options :config))))))
|
||||
(handler-bind ((unix-opts:unknown-option #'unknown-option))
|
||||
(unix-opts:get-opts))
|
||||
(unix-opts:missing-arg (condition)
|
||||
(format t "option ~s needs an argument!~%"
|
||||
(unix-opts:option condition)))
|
||||
(unix-opts:arg-parser-failed (condition)
|
||||
(format t "cannot parse ~s as argument of ~s~%"
|
||||
(unix-opts:raw-arg condition)
|
||||
(unix-opts:option condition)))
|
||||
(unix-opts:missing-required-option (con)
|
||||
(format t "~a~%" con)
|
||||
(unix-opts:exit 1)))
|
||||
(cond
|
||||
((getf options :help) (unix-opts:describe :usage-of "geml-server"))
|
||||
((getf options :version)
|
||||
(format t "~a~%" (asdf:component-version (asdf:find-system 'geml))))
|
||||
(t (let ((settings (cl-ini:parse-ini (getf options :config))))
|
||||
(start-server settings))))))
|
||||
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
:root
|
||||
:section (intern (string-upcase domain)
|
||||
:keyword)))
|
||||
;; TODO use path library?
|
||||
;; http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/sec_the_filen_s_dictionary.html
|
||||
(filename (probe-file (concatenate 'string
|
||||
root
|
||||
(resolve-index path)))))
|
||||
|
@ -38,29 +36,62 @@
|
|||
(format conn "51 ~a does not exist for ~a~%" path domain)))
|
||||
(force-output conn))
|
||||
|
||||
(defun start-server (settings-file)
|
||||
(let* ((settings (cl-ini:parse-ini settings-file))
|
||||
(host (or (cl-ini:ini-value settings :host)
|
||||
(defun ensure-certs (settings certs-dir)
|
||||
(mapcar #'(lambda (setting)
|
||||
(unless (eq (car setting) :global)
|
||||
(let* ((domain (string-downcase (symbol-name (car setting))))
|
||||
(key (concatenate 'string
|
||||
(namestring certs-dir)
|
||||
domain ".key"))
|
||||
(cert (concatenate 'string
|
||||
(namestring certs-dir)
|
||||
domain ".crt"))
|
||||
(missing-certs (or (not (probe-file key))
|
||||
(not (probe-file cert))))
|
||||
(openssl-cmd (format nil
|
||||
"openssl req -x509 ~
|
||||
-newkey rsa:4096 ~
|
||||
-sha256 ~
|
||||
-days 3650 -nodes ~
|
||||
-subj \"/CN=~a\" ~
|
||||
-keyout ~a ~
|
||||
-out ~a"
|
||||
;; TODO -addext \"subjectAltName=DNS:~a\""
|
||||
domain
|
||||
key
|
||||
cert)))
|
||||
(cond
|
||||
(missing-certs
|
||||
(progn (format t "geml is creating TLS certs for ~a (~a)~%"
|
||||
domain openssl-cmd)
|
||||
(uiop:run-program openssl-cmd)))
|
||||
(t
|
||||
(format t "geml found TLS certs for ~a~%" domain)))
|
||||
setting)))
|
||||
settings))
|
||||
|
||||
(defun start-server (settings)
|
||||
(let* ((host (or (cl-ini:ini-value settings :host)
|
||||
"0.0.0.0"))
|
||||
(port (or (cl-ini:ini-value settings :port)
|
||||
1965))
|
||||
(key (probe-file (cl-ini:ini-value settings :key)))
|
||||
(cert (probe-file (cl-ini:ini-value settings :cert))))
|
||||
(certs-dir (probe-file (or (cl-ini:ini-value settings :certs-dir)
|
||||
"/var/lib/geml"))))
|
||||
(ensure-certs settings certs-dir)
|
||||
(cond
|
||||
((not cert) (format t "geml cannot read cert using path defined in ~a~%" settings-file))
|
||||
((not key) (format t "geml cannot read key using path defined in ~a~%" settings-file))
|
||||
(t (let ((server (usocket:socket-listen host port)))
|
||||
(format t "geml booted on ~a:~a (~a)~%"
|
||||
(format t "geml booted on ~a:~a~%"
|
||||
host
|
||||
port
|
||||
settings-file)
|
||||
port)
|
||||
(unwind-protect
|
||||
(loop (let* ((socket (usocket:socket-accept server))
|
||||
(cert (concatenate 'string certs-dir domain ".crt"))
|
||||
(key (concatenate 'string certs-dir domain ".key"))
|
||||
(conn (cl+ssl:make-ssl-server-stream
|
||||
(usocket:socket-stream socket)
|
||||
:external-format '(:utf-8 :eol-style :crlf)
|
||||
:certificate (namestring cert)
|
||||
:key (namestring key))))
|
||||
:certificate cert
|
||||
:key key)))
|
||||
(unwind-protect (handle-request conn settings)
|
||||
(close conn))))
|
||||
(format t "geml is shutting down...~%")
|
||||
|
|
Loading…
Reference in a new issue