From 27349c51ab7478ca0800f9df0eab3ace7b1314a3 Mon Sep 17 00:00:00 2001 From: Tinnus Napbus Date: Sun, 4 Sep 2022 02:58:09 +1200 Subject: [PATCH] eyre: add auto-https redirects if a cert is configured and a secure port is live it will set the redirect flag in http-config.state. When it gets a ++request it will return a 301 redirect to https://[host]/[path] if: 1. not already secure 2. redirect flag set 3. secure port live 4. is not requesting /.well-known/acme-challenge/... 5. the host is in domains.state It will not happen if forwarded-secured, localhost, local loopback, ip addresses or domains not in domains.state. in ++load it checks the secure port is live and a cert is set and enables it if so (for people who already use in-urbit letencrypt) %rule %cert tasks also toggle it (only turning it on if secure port live) %live tasks also toggle it (only turning it on if cert set) Have tested with a couple of ships and seems to work fine. This is useful in combination with pyry's auto arvo.network dns config system - can finally get rid of reverse proxies entirely. --- pkg/arvo/sys/vane/eyre.hoon | 69 ++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index 562c33b465..3ee68f826e 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -551,6 +551,18 @@ (easy ~) == == +:: +host-parser: returns [(unit userinfo=@t) (unit port=@ud) host:eyre] +:: +++ host-parser + |^ + ;~(plug userinfo thor:de-purl:html) + ++ userinfo (punt (cook crip ;~(sfix (star userinfo-char) pat))) + ++ userinfo-char ;~(pose col unreserved sub-delims pct-encoded) + ++ unreserved ;~(pose aln hep dot cab sig) + ++ sub-delims ;~(pose zap buc pam soq pal par tar lus com mic tis) + ++ pct-encoded (cook crip ;~(plug cen hex-char hex-char (easy ~))) + ++ hex-char ;~(pose nud (shim 'a' 'f') (shim 'A' 'F')) + -- :: +per-server-event: per-event server core :: ++ per-server-event @@ -580,7 +592,7 @@ :: ++ request |= [secure=? =address =request:http] - ^- [(list move) server-state] + |^ ^- [(list move) server-state] =* headers header-list.request :: for requests from localhost, respect the "forwarded" header :: @@ -602,6 +614,35 @@ [action [authenticated secure address request] ~ 0] =. connections.state (~(put by connections.state) duct connection) + :: redirect to https if insecure, redirects enabled, + :: secure port live, not an acme challenge and host + :: is in domains.state + :: + ?: ?& !secure + redirect.http-config.state + ?=(^ secure.ports.state) + ?! ?= [* [%'.well-known' %acme-challenge *] *] + (parse-request-line url.request) + (host-in-domains host) + == + =/ location=@t + ;: (cury cat 3) + 'https://' + (need host) + ?: =(443 u.secure.ports.state) + '' + (crip ":{(a-co:co u.secure.ports.state)}") + ?: ?=([[~ ~] ~] (parse-request-line url.request)) + '/' + url.request + == + %- handle-response + :* %start + :- status-code=301 + headers=['location' location]~ + data=~ + complete=%.y + == :: figure out whether this is a cors request, :: whether the origin is approved or not, :: and maybe add it to the "pending approval" set @@ -712,6 +753,20 @@ %^ return-static-data-on-duct 404 'text/html' (error-page 404 authenticated url.request ~) == + :: test if host header is valid and turf in domains.state + :: + ++ host-in-domains + |= raw-host=(unit @t) + ^- ? + ?~ raw-host | + =/ auth=(unit [* * =host:eyre]) + (rush u.raw-host host-parser) + ?& ?=(^ auth) + ?=(%.y -.host.u.auth) + (~(has in domains.state) p.host.u.auth) + == + -- +:: :: +handle-scry: respond with scry result, 404 or 500 :: ++ handle-scry @@ -2271,6 +2326,10 @@ :: %live =. ports.server-state.ax +.task + :: enable http redirects if https port live and cert set + :: + =. redirect.http-config.server-state.ax + &(?=(^ secure.task) ?=(^ secure.http-config.server-state.ax)) [~ http-server-gate] :: %rule: updates our http configuration :: @@ -2283,6 +2342,8 @@ ?: =(secure.config cert.http-rule.task) [~ http-server-gate] =. secure.config cert.http-rule.task + =. redirect.config + &(?=(^ secure.ports.server-state.ax) ?=(^ cert.http-rule.task)) :_ http-server-gate =* out-duct outgoing-duct.server-state.ax ?~ out-duct ~ @@ -2533,6 +2594,12 @@ ++ load |= old=axle ^+ ..^$ + :: enable https redirects if certificate configured + :: + =. redirect.http-config.server-state.old + ?& ?=(^ secure.ports.server-state.old) + ?=(^ secure.http-config.server-state.old) + == ..^$(ax old) :: +stay: produce current state ::