Fork me on GitHub
#luminus
<
2022-08-27
>
Edward Ciafardini19:08:20

I have my luminus app deployed to heroku with an SSL certificate- so going to https://www.my http://app.com (fake link) works and shows the lock icon in browser But if I access the link without the https:// appended to the URL, http is used and there is an “unsecured connection” warning. Is there a way in luminus to redirect all requests to my URL to use https?

Eugen08:08:16

be careful with hsts, browsers are kind of strict from what I remember

Eugen08:08:14

bottom: line, you need to do it in your app. A high level solution is: Add a route / middleware ?! that checks schema = http . when that happens, either send HSTS and/or redirect to http . Maybe you can find a library that implements this logic ?!

Edward Ciafardini22:09:27

Thanks - I think I found something 😁

Eugen08:09:02

please share

Marius08:09:48

@U01TLNH6ULD I have the same issue and would also be interested in your solution 🙂

Ted Ciafardini11:09:00

this is what I found: https://github.com/ring-clojure/ring-ssl I haven’t tried it yet- but it’s on my todo list.

Ted Ciafardini11:09:05

Let me know if you have any luck 🙂

Marius11:09:44

Cool, thank you very much!

Marius12:09:49

Actually there is a much easier way, almost trivial. It is possible to enable a Strict-Transport-Security header and redirect to HTTPS using the Ring config. No need to add ring-ssl. Just add the lines (assoc-in [:security :hsts] true) (assoc-in [:security :ssl-redirect] true) like this in your middleware.clj:

(wrap-defaults
        (-> site-defaults
            (assoc-in [:security :anti-forgery] true) 
            (assoc-in [:session :store] session/store)
            (assoc-in [:security :hsts] true)
            (assoc-in [:security :ssl-redirect] true)))
      wrap-internal-error))

💫 1
Ted Ciafardini12:09:55

awesome. Trivial if you know about it :man-shrugging: - where did you find this? thanks!

Marius12:09:07

True 🙂 I was looking into what function wrap-defaults from ring.middleware.defaults does, that’s where I found it.

thanks2 1
Marius12:09:06

Only problem right now is that it is enabled in dev mode as well, and https://localhost:3000 does not work

Ted Ciafardini12:09:19

I was wondering if that would be a problem

Ted Ciafardini12:09:48

Probably a cond-> that checks the env var

Ted Ciafardini12:09:53

let me try something

Marius12:09:04

Yeah… (assoc-in [:security :ssl-redirect] (get defaults :ssl-redirect true))))

Marius12:09:21

and then :ssl-redirect false in env.clj

Ted Ciafardini12:09:47

I had a gnarly cond-> going lol

Marius12:09:57

It’s probably better to disable HSTS for localhost as well

Marius12:09:32

I had it enabled and got redirected (although broken), but now my browser will immediately redirect to HTTPS due to HSTS… the max-age is set for 1 year — I guess I should find out how to reset this 😂

Ted Ciafardini13:09:55

Are you sure about (get defaults :hsts true)? This will default to true even if it isn’t present

Marius13:09:38

My idea was that it isn’t present on production, therefore to enable it on production, it must be true

Ted Ciafardini13:09:04

I’m trying this out:

(defn wrap-base [handler]
  (let [config-file (:conf (source/from-system-props))]
   (-> ((:middleware defaults) handler)
      (wrap-defaults
       (-> site-defaults
           (assoc-in [:security :anti-forgery] false)
           (assoc-in [:session :store] (ttl-memory-store (* 60 30)))
           ;Enable HTTPS redirect...
           ;TODO - look into these settings and what they mean:
           (assoc-in [:security :hsts] (if (= config-file "dev-config.edn")
                                         false
                                         true))
           (assoc-in [:security :ssl-redirect] (if (= config-file "dev-config.edn")
                                                 false
                                                 true))))
      wrap-internal-error)))

Ted Ciafardini13:09:19

ack - assoc’ing in ssl-redirect broke my page when deployed 🌚

Marius13:09:52

your version looks a bit too complicated in checking the config file name to figure out whether it’s dev or not

Marius13:09:25

what exactly broke your page?

Ted Ciafardini13:09:47

This page isn't  redirected you too many times.
Try clearing your cookies.
ERR_TOO_MANY_REDIRECTS

Ted Ciafardini13:09:20

You don’t have this problem in production?

Ted Ciafardini13:09:04

“More than 20 redirections”

Marius13:09:46

You’re faster than me, I’m deploying… 😄

Marius13:09:56

Ah ok, seems like the App does not know it’s being accessed via HTTPS already and therefore already redirects. I will have the same issue then.

Ted Ciafardini13:09:05

Take a look at this defaults.clj - the wrap-defaults function

Ted Ciafardini13:09:27

I think it’s already doing this for us, we just need to set up the config correctly

Marius13:09:33

Yeah, I was just looking at it too, try setting :proxy to true to enable wrap-forwarded-scheme

Ted Ciafardini13:09:11

yah, deploying now to see if it works 🤞

Marius13:09:21

Works for me now 👍

Ted Ciafardini13:09:41

Can I see yr wrap-base fn?

Marius13:09:49

1st redirect is from the app (HTTP 301), on a subsequent visit it’s a browser redirect (HTTP 307)… as expected

Marius13:09:30

(defn wrap-base [handler]
  (-> ((:middleware defaults) handler)
      (wrap-defaults
        (-> site-defaults
            (assoc-in [:security :anti-forgery] true)
            (assoc-in [:session :store] session/store)
            (assoc-in  [:security :hsts] (get defaults :hsts true))
            (assoc-in [:security :ssl-redirect] (get defaults :ssl-redirect true))
            (assoc-in [:proxy] (get defaults :proxy true))))
      wrap-internal-error))

Marius13:09:15

and my config in env.clj

:hsts false
   :ssl-redirect false
   :proxy false})

Ted Ciafardini13:09:50

how did you know defaults would have the vals from config?

Marius13:09:44

because I added them in env.clj

Ted Ciafardini13:09:11

Can I see that?

Marius13:09:01

(ns myapp.env
  (:require
    [selmer.parser :as parser]
    [clojure.tools.logging :as log]
    [myapp.dev-middleware :refer [wrap-dev]]))

(def defaults
  {:init
   (fn []
     (parser/cache-off!)
     (log/info "\n-=[myapp started successfully using the development profile]=-"))
   :stop
   (fn []
     (log/info "\n-=[myapp has shut down successfully]=-"))
   :middleware wrap-dev
   :hsts false
   :ssl-redirect false
   :proxy false})

Ted Ciafardini13:09:50

ahhhh, I see now

Ted Ciafardini13:09:21

I was trying to pull these from the config file. I wonder if there’s a way to do that. This works though

Marius13:09:50

I would only pull things from a config file which need to change without recompilation and can be read from the environment, so e.g. database URL

Marius13:09:17

but in this case the production build will never have HTTPS switched off — I guess 🙂

Ted Ciafardini13:09:41

Ok, cool. This is the first website I’ve ever made, so I don’t really know best practices

Ted Ciafardini13:09:49

Thanks for all your help

Marius13:09:53

if you look at core.clj, there is a line like this: (update :port #(or (-> env :options :port) %)) This is reading the port from the config file

Ted Ciafardini13:09:36

I set up a prod-only DATABASE_URL for the postgres instance by the skin of my teeth