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 \
|
--owner geml \
|
||||||
--group geml \
|
--group geml \
|
||||||
--mode 644 \
|
--mode 644 \
|
||||||
geml.ini
|
geml.example.ini
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
id -u geml &>/dev/null && userdel geml
|
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
|
## /etc/geml/geml.ini
|
||||||
|
|
||||||
geml requires `cert` and `key` to be configured before it will run. And will
|
geml will have nothing to serve until you configure at least one domain and
|
||||||
have nothing to serve until you configure at least one domain and root. See
|
root.
|
||||||
[`geml.ini`](./geml.ini) as an example.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
cert = /var/lib/geml/localhost.crt
|
|
||||||
key = /var/lib/geml/localhost.key
|
|
||||||
|
|
||||||
[my.gmi.capsule]
|
[my.gmi.capsule]
|
||||||
root = /srv/gmi
|
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
|
## Usage
|
||||||
|
|
||||||
### Standalone Executable
|
|
||||||
|
|
||||||
To get an executable `bin/geml-server` run `make`.
|
To get an executable `bin/geml-server` run `make`.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make && bin/geml-server -h
|
make && bin/geml-server -h
|
||||||
```
|
```
|
||||||
|
|
||||||
### Systemd
|
### Systemd
|
||||||
|
|
||||||
Install the build to `/usr/local/bin/geml-server` with
|
To install the build to `/usr/local/bin/geml-server` use `sudo make install`
|
||||||
`sudo make install` which will also wire up systemd so you can `sudo systemctl
|
which will also wire up systemd so you can `sudo systemctl enable --now geml`
|
||||||
enable --now geml`
|
|
||||||
|
|
||||||
You'll need to `sudo systemctl restart geml` whenever you update
|
You'll need to `sudo systemctl restart geml` whenever you update
|
||||||
`/etc/geml/geml.ini`... you did do that didn't you?
|
`/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
|
### yeet it
|
||||||
|
|
||||||
To undo all that run these:
|
To undo all that run these:
|
||||||
|
@ -59,12 +46,5 @@ To undo all that run these:
|
||||||
sudo make uninstall
|
sudo make uninstall
|
||||||
make clean
|
make clean
|
||||||
```
|
```
|
||||||
|
**NOTE**: The automatically created TLS certs will still be at `/var/lib/geml`
|
||||||
### SBCL
|
you can (re)use those elsewhere or also just delete them.
|
||||||
|
|
||||||
Start sbcl with proper readline support: `rlwrap sbcl`
|
|
||||||
|
|
||||||
```lisp
|
|
||||||
(ql:quickload "geml")
|
|
||||||
(gemini.server:start-server "/etc/geml/geml.ini")
|
|
||||||
```
|
|
||||||
|
|
4
geml.ini
4
geml.ini
|
@ -2,9 +2,7 @@
|
||||||
;; see usocket: https://github.com/usocket/usocket#api-definition
|
;; see usocket: https://github.com/usocket/usocket#api-definition
|
||||||
;; host = 0.0.0.0
|
;; host = 0.0.0.0
|
||||||
;; port = 1965
|
;; port = 1965
|
||||||
|
;; certs-dir = /var/lib/geml/
|
||||||
cert = /var/lib/geml/localhost.crt
|
|
||||||
key = /var/lib/geml/localhost.key
|
|
||||||
|
|
||||||
[localhost]
|
[localhost]
|
||||||
root = /srv/gmi
|
root = /srv/gmi
|
||||||
|
|
35
src/bin.lisp
35
src/bin.lisp
|
@ -24,19 +24,22 @@
|
||||||
(defun bin ()
|
(defun bin ()
|
||||||
(multiple-value-bind (options free-args)
|
(multiple-value-bind (options free-args)
|
||||||
(handler-case
|
(handler-case
|
||||||
(handler-bind ((opts:unknown-option #'unknown-option))
|
(handler-bind ((unix-opts:unknown-option #'unknown-option))
|
||||||
(opts:get-opts))
|
(unix-opts:get-opts))
|
||||||
(opts:missing-arg (condition)
|
(unix-opts:missing-arg (condition)
|
||||||
(format t "option ~s needs an argument!~%"
|
(format t "option ~s needs an argument!~%"
|
||||||
(opts:option condition)))
|
(unix-opts:option condition)))
|
||||||
(opts:arg-parser-failed (condition)
|
(unix-opts:arg-parser-failed (condition)
|
||||||
(format t "cannot parse ~s as argument of ~s~%"
|
(format t "cannot parse ~s as argument of ~s~%"
|
||||||
(opts:raw-arg condition)
|
(unix-opts:raw-arg condition)
|
||||||
(opts:option condition)))
|
(unix-opts:option condition)))
|
||||||
(opts:missing-required-option (con)
|
(unix-opts:missing-required-option (con)
|
||||||
(format t "~a~%" con)
|
(format t "~a~%" con)
|
||||||
(opts:exit 1)))
|
(unix-opts:exit 1)))
|
||||||
(cond
|
(cond
|
||||||
((getf options :help) (opts:describe :usage-of "geml-server"))
|
((getf options :help) (unix-opts:describe :usage-of "geml-server"))
|
||||||
((getf options :version) (format t "~a~%" (asdf:component-version (asdf:find-system 'geml))))
|
((getf options :version)
|
||||||
(t (start-server (getf options :config))))))
|
(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
|
:root
|
||||||
:section (intern (string-upcase domain)
|
:section (intern (string-upcase domain)
|
||||||
:keyword)))
|
: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
|
(filename (probe-file (concatenate 'string
|
||||||
root
|
root
|
||||||
(resolve-index path)))))
|
(resolve-index path)))))
|
||||||
|
@ -38,29 +36,62 @@
|
||||||
(format conn "51 ~a does not exist for ~a~%" path domain)))
|
(format conn "51 ~a does not exist for ~a~%" path domain)))
|
||||||
(force-output conn))
|
(force-output conn))
|
||||||
|
|
||||||
(defun start-server (settings-file)
|
(defun ensure-certs (settings certs-dir)
|
||||||
(let* ((settings (cl-ini:parse-ini settings-file))
|
(mapcar #'(lambda (setting)
|
||||||
(host (or (cl-ini:ini-value settings :host)
|
(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"))
|
"0.0.0.0"))
|
||||||
(port (or (cl-ini:ini-value settings :port)
|
(port (or (cl-ini:ini-value settings :port)
|
||||||
1965))
|
1965))
|
||||||
(key (probe-file (cl-ini:ini-value settings :key)))
|
(certs-dir (probe-file (or (cl-ini:ini-value settings :certs-dir)
|
||||||
(cert (probe-file (cl-ini:ini-value settings :cert))))
|
"/var/lib/geml"))))
|
||||||
|
(ensure-certs settings certs-dir)
|
||||||
(cond
|
(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)))
|
(t (let ((server (usocket:socket-listen host port)))
|
||||||
(format t "geml booted on ~a:~a (~a)~%"
|
(format t "geml booted on ~a:~a~%"
|
||||||
host
|
host
|
||||||
port
|
port)
|
||||||
settings-file)
|
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(loop (let* ((socket (usocket:socket-accept server))
|
(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
|
(conn (cl+ssl:make-ssl-server-stream
|
||||||
(usocket:socket-stream socket)
|
(usocket:socket-stream socket)
|
||||||
:external-format '(:utf-8 :eol-style :crlf)
|
:external-format '(:utf-8 :eol-style :crlf)
|
||||||
:certificate (namestring cert)
|
:certificate cert
|
||||||
:key (namestring key))))
|
:key key)))
|
||||||
(unwind-protect (handle-request conn settings)
|
(unwind-protect (handle-request conn settings)
|
||||||
(close conn))))
|
(close conn))))
|
||||||
(format t "geml is shutting down...~%")
|
(format t "geml is shutting down...~%")
|
||||||
|
|
Loading…
Reference in a new issue