Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
Basic Auth Configuration (#45)
Browse files Browse the repository at this point in the history
* Basic Basic Auth

* Allow username and password to be passed in via env vars

* Add test spec for basic auth returning 401

* Update README with Basic Auth config info

* Add .htpasswd to test fixture

* Set env vars in test

* Request the file that actually exists, maybe?

* Is context the key?

* Perhaps app.run needs to come to the party too?

* Move `auth_basic` from `location` to `server`

* Set `basic_auth` as true if env `basic_auth_username` exists

* Fix htpasswd generation when basic_auth is false

* Append env password instead truncating

* Fix typo

* Fix config example in README
  • Loading branch information
randallagordon authored and Dhaulagiri committed Apr 3, 2019
1 parent 2d643c5 commit bde9522
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,20 @@ You can redirect all HTTP requests to HTTPS.
}
```

#### Basic Authentication

You can enable Basic Authentication so all requests require authentication.

```
{
"basic_auth": true
}
```

This will generate `.htpasswd` using environment variables `BASIC_AUTH_USERNAME` and `BASIC_AUTH_PASSWORD` if they are present. Otherwise it will use a standard `.htpasswd` file present in the `app` directory.

Passwords set via `BASIC_AUTH_PASSWORD` can be generated using OpenSSL or Apache Utils. For instance: `openssl passwd -apr1`.

#### Proxy Backends
For single page web applications like Ember, it's common to back the application with another app that's hosted on Heroku. The down side of separating out these two applications is that now you have to deal with CORS. To get around this (but at the cost of some latency) you can have the static buildpack proxy apps to your backend at a mountpoint. For instance, we can have all the api requests live at `/api/` which actually are just requests to our API server.

Expand Down
3 changes: 3 additions & 0 deletions scripts/boot
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ esac

"${HERE}/config/make-config"

# Create .htpasswd if BASIC_AUTH_USERNAME and BASIC_AUTH_PASSWORD are provided
"${HERE}/config/make-htpasswd"

# make a shared pipe; we'll write the name of the process that exits to it once
# that happens, and wait for that event below this particular call works on
# Linux and Mac OS (will create a literal ".XXXXXX" on Mac, but that doesn't
Expand Down
6 changes: 6 additions & 0 deletions scripts/config/lib/nginx_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class NginxConfig
encoding: "UTF-8",
clean_urls: false,
https_only: false,
basic_auth: false,
basic_auth_htpasswd_path: "/app/.htpasswd",
worker_connections: 512,
resolver: "8.8.8.8",
logging: {
Expand Down Expand Up @@ -45,6 +47,10 @@ def initialize(json_file)
json["clean_urls"] ||= DEFAULT[:clean_urls]
json["https_only"] ||= DEFAULT[:https_only]

json["basic_auth"] = true unless ENV['BASIC_AUTH_USERNAME'].nil?
json["basic_auth"] ||= DEFAULT[:basic_auth]
json["basic_auth_htpasswd_path"] ||= DEFAULT[:basic_auth_htpasswd_path]

json["routes"] ||= {}
json["routes"] = NginxConfigUtil.parse_routes(json["routes"])

Expand Down
16 changes: 16 additions & 0 deletions scripts/config/make-htpasswd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env ruby

require 'json'

USER_CONFIG = "/app/static.json"

config = {}
config = JSON.parse(File.read(USER_CONFIG)) if File.exist?(USER_CONFIG)

HTPASSWD = config["basic_auth_htpasswd_path"] || '/app/.htpasswd'
USERNAME = ENV["BASIC_AUTH_USERNAME"]
PASSWORD = ENV["BASIC_AUTH_PASSWORD"]

htpasswd = "#{USERNAME}:#{PASSWORD}" unless (USERNAME.nil? || PASSWORD.nil?)

File.open(HTPASSWD, 'a') { |file| file.puts(htpasswd) } if !htpasswd.nil?
5 changes: 5 additions & 0 deletions scripts/config/templates/nginx.conf.erb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ http {
resolver <%= resolver %>;
<% end %>
<% if basic_auth %>
auth_basic "Restricted";
auth_basic_user_file <%= basic_auth_htpasswd_path %>;
<% end %>

location / {
mruby_post_read_handler /app/bin/config/lib/ngx_mruby/headers.rb cache;
mruby_set $fallback /app/bin/config/lib/ngx_mruby/routes_fallback.rb cache;
Expand Down
1 change: 1 addition & 0 deletions spec/fixtures/basic_auth/.htpasswd
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test:$apr1$Dnavu2z9$ZFxQn/mXVQoeYGD.tA2bW/
1 change: 1 addition & 0 deletions spec/fixtures/basic_auth/public_html/foo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foobar
3 changes: 3 additions & 0 deletions spec/fixtures/basic_auth/static.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"basic_auth": true
}
34 changes: 34 additions & 0 deletions spec/simple_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,40 @@
end
end

describe "basic_auth" do
context "static.json without basic_auth key" do
let(:name) { "hello_world" }

let(:env) {
{
"BASIC_AUTH_USERNAME" => "test",
"BASIC_AUTH_PASSWORD" => "$apr1$Dnavu2z9$ZFxQn/mXVQoeYGD.tA2bW/"
}
}

it "should require authentication" do
response = app.get("/index.html")
expect(response.code).to eq("401")
end
end

context "static.json with basic_auth key and .htpasswd" do
let(:name) { "basic_auth" }

let(:env) {
{
"BASIC_AUTH_USERNAME" => "test",
"BASIC_AUTH_PASSWORD" => "$apr1$/pb2/xQR$cn7UPcTOLymIH1ZMe.NfO."
}
}

it "should require authentication" do
response = app.get("/foo.html")
expect(response.code).to eq("401")
end
end
end

describe "custom error pages" do
let(:name) { "custom_error_pages" }

Expand Down

0 comments on commit bde9522

Please sign in to comment.