Skip to content

Commit b3a8abc

Browse files
committed
Redirect all web content to HTTPS by default.
This adjusts the existing config options so that API Umbrella redirects all web-app and website requests to the HTTPS version by default (while we had configured most of our instances to do this already, it seems time to make this the default behavior so there's not the weird page-specific split in behavior). It also adds a new configuration option so that any "not found" responses are redirected to HTTPS. API behavior remains the same (we default to forcing HTTPS, but not via redirects). See 18F/api.data.gov#430 for details about the "not found" redirecting.
1 parent 17bc65c commit b3a8abc

File tree

10 files changed

+293
-31
lines changed

10 files changed

+293
-31
lines changed

config/default.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ router:
130130
ip_connections_log_level: error
131131
web_app_host: "*"
132132
web_app_backend_regex: "^/(admin|admins|web-assets)(/|$)"
133-
web_app_backend_required_https_regex: "^/(admin|admins)(/|$)"
134-
website_backend_required_https_regex_default: "^/(account|signup|contact)(/|$)"
133+
web_app_backend_required_https_regex: "^.*"
134+
website_backend_required_https_regex_default: "^.*"
135+
redirect_not_found_to_https: true
135136
rsyslog:
136137
host: 127.0.0.1
137138
port: 14014

src/api-umbrella/proxy/error_handler.lua

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
local is_hash = require "api-umbrella.utils.is_hash"
21
local deep_merge_overwrite_arrays = require "api-umbrella.utils.deep_merge_overwrite_arrays"
2+
local httpsify_current_url = require "api-umbrella.utils.httpsify_current_url"
3+
local is_hash = require "api-umbrella.utils.is_hash"
34
local lustache = require "lustache"
45
local mustache_unescape = require "api-umbrella.utils.mustache_unescape"
56
local path = require "pl.path"
@@ -122,6 +123,20 @@ local function render_template(template, data, format, strip_whitespace)
122123
end
123124

124125
return function(denied_code, settings, extra_data)
126+
-- Redirect "not_found" errors to HTTPS.
127+
--
128+
-- Since these errors aren't subject to an API Backend's HTTPS requirements
129+
-- (where we might return the "https_required" error), this helps ensure that
130+
-- requests to unknown location (neither API or website backend) are
131+
-- redirected to HTTPS like the rest of our non-API content. This ensures
132+
-- HTTPS redirects are in place for the root request on custom domains
133+
-- without a website or API at the root.
134+
if denied_code == "not_found" and config["router"]["redirect_not_found_to_https"] then
135+
if ngx.ctx.protocol ~= "https" then
136+
return ngx.redirect(httpsify_current_url(), ngx.HTTP_MOVED_PERMANENTLY)
137+
end
138+
end
139+
125140
-- Store the gatekeeper rejection code for logging.
126141
ngx.ctx.gatekeeper_denied_code = denied_code
127142

test/processes/test_reloads.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def test_file_based_config_changes_updates_templates
171171
end
172172

173173
def test_file_based_config_changes_updates_apis
174-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/file-config/info/", http_options)
174+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/file-config/info/", http_options)
175175
assert_response_code(404, response)
176176

177177
override_config({
@@ -187,13 +187,13 @@ def test_file_based_config_changes_updates_apis
187187
},
188188
],
189189
}, "--router") do
190-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/file-config/info/", http_options)
190+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/file-config/info/", http_options)
191191
assert_response_code(200, response)
192192
data = MultiJson.load(response.body)
193193
assert_equal(data["headers"]["x-test-file-config"], "foo")
194194
end
195195

196-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/file-config/info/", http_options)
196+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/file-config/info/", http_options)
197197
assert_response_code(404, response)
198198
end
199199

test/proxy/logging/test_basics.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ def test_case_sensitivity
510510
end
511511

512512
def test_does_not_website_backend_requests
513-
response = Typhoeus.get("http://127.0.0.1:9080/", log_http_options)
513+
response = Typhoeus.get("https://127.0.0.1:9081/", log_http_options)
514514
assert_response_code(200, response)
515515

516516
error = assert_raises Timeout::Error do
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
require_relative "../../test_helper"
2+
3+
class Test::Proxy::Routing::TestHttpsConfig < Minitest::Test
4+
include ApiUmbrellaTestHelpers::Setup
5+
include ApiUmbrellaTestHelpers::AdminAuth
6+
include Minitest::Hooks
7+
8+
def setup
9+
super
10+
setup_server
11+
once_per_class_setup do
12+
override_config_set({
13+
"router" => {
14+
"web_app_backend_required_https_regex" => "^/admin/web-app-https-test",
15+
"website_backend_required_https_regex_default" => "^/website-https-test",
16+
"redirect_not_found_to_https" => false,
17+
"web_app_host" => "127.0.0.1",
18+
},
19+
}, "--router")
20+
end
21+
end
22+
23+
def after_all
24+
super
25+
override_config_reset("--router")
26+
end
27+
28+
def test_custom_web_app_regex
29+
response = Typhoeus.get("http://127.0.0.1:9080/admin/", keyless_http_options)
30+
assert_response_code(200, response)
31+
assert_match(%r{<script src="assets/api-umbrella-admin-ui-\w+\.js"}, response.body)
32+
33+
response = Typhoeus.get("http://127.0.0.1:9080/admin/web-app-https-test", keyless_http_options)
34+
assert_response_code(301, response)
35+
assert_equal("https://127.0.0.1:9081/admin/web-app-https-test", response.headers["Location"])
36+
end
37+
38+
def test_custom_website_backend_regex
39+
response = Typhoeus.get("http://127.0.0.1:9080/", keyless_http_options)
40+
assert_response_code(200, response)
41+
assert_match("Your API Site Name", response.body)
42+
43+
response = Typhoeus.get("http://127.0.0.1:9080/website-https-test", keyless_http_options)
44+
assert_response_code(301, response)
45+
assert_equal("https://127.0.0.1:9081/website-https-test", response.headers["Location"])
46+
end
47+
48+
def test_not_found_https_disabled
49+
response = Typhoeus.get("http://127.0.0.1:9080/api-umbrella/v1/state.json", http_options.deep_merge(admin_token).deep_merge({
50+
:headers => {
51+
"Host" => "#{unique_test_id}-unknown.foo",
52+
},
53+
}))
54+
assert_response_code(404, response)
55+
assert_equal("application/json", response.headers["content-type"])
56+
assert_match("NOT_FOUND", response.body)
57+
end
58+
end

test/proxy/routing/test_website.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def setup
1010
end
1111

1212
def test_default_website
13-
response = Typhoeus.get("http://127.0.0.1:9080/", keyless_http_options)
13+
response = Typhoeus.get("https://127.0.0.1:9081/", keyless_http_options)
1414
assert_response_code(200, response)
1515
assert_match("Your API Site Name", response.body)
1616

@@ -19,7 +19,11 @@ def test_default_website
1919
assert_match("API Key Signup", response.body)
2020
end
2121

22-
def test_signup_https_redirect
22+
def test_https_redirect
23+
response = Typhoeus.get("http://127.0.0.1:9080/", keyless_http_options)
24+
assert_response_code(301, response)
25+
assert_equal("https://127.0.0.1:9081/", response.headers["location"])
26+
2327
response = Typhoeus.get("http://127.0.0.1:9080/signup/", keyless_http_options)
2428
assert_response_code(301, response)
2529
assert_equal("https://127.0.0.1:9081/signup/", response.headers["location"])

test/proxy/test_forwarded_port_headers.rb

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ def test_default_headers
4646
when :admin_oauth2_http
4747
assert_response_code(301, response)
4848
assert_equal("https://127.0.0.1:9081/admins/auth/google_oauth2", response.headers["Location"])
49-
when :website_https, :website_http
49+
when :website_https
5050
assert_response_code(200, response)
51+
when :website_http
52+
assert_response_code(301, response)
53+
assert_equal("https://127.0.0.1:9081/", response.headers["Location"])
5154
when :website_signup_https
5255
assert_response_code(200, response)
5356
when :website_signup_http
@@ -75,8 +78,11 @@ def test_forwarded_port
7578
when :admin_oauth2_http
7679
assert_response_code(301, response)
7780
assert_equal("https://127.0.0.1:9081/admins/auth/google_oauth2", response.headers["Location"])
78-
when :website_https, :website_http
81+
when :website_https
7982
assert_response_code(200, response)
83+
when :website_http
84+
assert_response_code(301, response)
85+
assert_equal("https://127.0.0.1:9081/", response.headers["Location"])
8086
when :website_signup_https
8187
assert_response_code(200, response)
8288
when :website_signup_http
@@ -100,7 +106,8 @@ def test_forwarded_proto_http
100106
assert_response_code(301, response)
101107
assert_equal("https://127.0.0.1:9081/admins/auth/google_oauth2", response.headers["Location"])
102108
when :website_https, :website_http
103-
assert_response_code(200, response)
109+
assert_response_code(301, response)
110+
assert_equal("https://127.0.0.1:9081/", response.headers["Location"])
104111
when :website_signup_https, :website_signup_http
105112
assert_response_code(301, response)
106113
assert_equal("https://127.0.0.1:9081/signup/", response.headers["Location"])
@@ -145,7 +152,8 @@ def test_forwarded_proto_http_and_port
145152
assert_response_code(301, response)
146153
assert_equal("https://127.0.0.1:9081/admins/auth/google_oauth2", response.headers["Location"])
147154
when :website_https, :website_http
148-
assert_response_code(200, response)
155+
assert_response_code(301, response)
156+
assert_equal("https://127.0.0.1:9081/", response.headers["Location"])
149157
when :website_signup_https, :website_signup_http
150158
assert_response_code(301, response)
151159
assert_equal("https://127.0.0.1:9081/signup/", response.headers["Location"])
@@ -194,8 +202,11 @@ def test_override_public_http_port
194202
when :admin_oauth2_http
195203
assert_response_code(301, response)
196204
assert_equal("https://127.0.0.1:9081/admins/auth/google_oauth2", response.headers["Location"])
197-
when :website_https, :website_http
205+
when :website_https
198206
assert_response_code(200, response)
207+
when :website_http
208+
assert_response_code(301, response)
209+
assert_equal("https://127.0.0.1:9081/", response.headers["Location"])
199210
when :website_signup_https
200211
assert_response_code(200, response)
201212
when :website_signup_http
@@ -227,8 +238,11 @@ def test_override_public_https_port
227238
when :admin_oauth2_http
228239
assert_response_code(301, response)
229240
assert_equal("https://127.0.0.1:3333/admins/auth/google_oauth2", response.headers["Location"])
230-
when :website_https, :website_http
241+
when :website_https
231242
assert_response_code(200, response)
243+
when :website_http
244+
assert_response_code(301, response)
245+
assert_equal("https://127.0.0.1:3333/", response.headers["Location"])
232246
when :website_signup_https
233247
assert_response_code(200, response)
234248
when :website_signup_http
@@ -260,7 +274,10 @@ def test_override_public_http_proto
260274
when :admin_oauth2_http
261275
assert_response_code(302, response)
262276
assert_oauth2_redirect_uri("https://127.0.0.1:9080/admins/auth/google_oauth2/callback", response)
263-
when :website_https, :website_http
277+
when :website_https
278+
assert_response_code(301, response)
279+
assert_equal("https://127.0.0.1:9081/", response.headers["Location"])
280+
when :website_http
264281
assert_response_code(200, response)
265282
when :website_signup_https
266283
assert_response_code(301, response)
@@ -293,7 +310,10 @@ def test_override_public_https_proto
293310
when :admin_oauth2_http
294311
assert_response_code(302, response)
295312
assert_oauth2_redirect_uri("https://127.0.0.1:9080/admins/auth/google_oauth2/callback", response)
296-
when :website_https, :website_http
313+
when :website_https
314+
assert_response_code(301, response)
315+
assert_equal("https://127.0.0.1:9081/", response.headers["Location"])
316+
when :website_http
297317
assert_response_code(200, response)
298318
when :website_signup_https
299319
assert_response_code(301, response)
@@ -327,8 +347,11 @@ def test_override_public_ports_defaults
327347
when :admin_oauth2_http
328348
assert_response_code(301, response)
329349
assert_equal("https://127.0.0.1/admins/auth/google_oauth2", response.headers["Location"])
330-
when :website_https, :website_http
350+
when :website_https
331351
assert_response_code(200, response)
352+
when :website_http
353+
assert_response_code(301, response)
354+
assert_equal("https://127.0.0.1/", response.headers["Location"])
332355
when :website_signup_https
333356
assert_response_code(200, response)
334357
when :website_signup_http

test/proxy/test_nginx_rewrites.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ def test_basic_rewrites
2525
],
2626
}, "--router") do
2727
# Basic rewrite
28-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/hello/rewrite?foo=bar", http_options)
28+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options)
2929
assert_response_code(301, response)
3030
assert_equal("https://example.com/something/?foo=bar", response.headers["location"])
3131

3232
# Advanced with replacements rewrite
33-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/hello/rewrite/el/7/file-0.6.0-1.el7.x86_64.rpm?foo=bar", http_options)
33+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite/el/7/file-0.6.0-1.el7.x86_64.rpm?foo=bar", http_options)
3434
assert_response_code(302, response)
3535
assert_equal("https://example.com/downloads/v0.6.0/file-0.6.0-1.el7.x86_64.rpm", response.headers["location"])
3636
end
@@ -52,20 +52,20 @@ def test_default_host
5252
],
5353
}, "--router") do
5454
# Known host without rewrites
55-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
55+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
5656
:headers => { "Host" => "default.foo" },
5757
}))
5858
assert_response_code(301, response)
5959
assert_equal("https://example.com/something/?foo=bar", response.headers["location"])
6060

6161
# Known host without rewrites
62-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
62+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
6363
:headers => { "Host" => "known.foo" },
6464
}))
6565
assert_response_code(404, response)
6666

6767
# Unknown host
68-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
68+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
6969
:headers => { "Host" => "unknown.foo" },
7070
}))
7171
assert_response_code(301, response)
@@ -85,14 +85,14 @@ def test_no_default_host
8585
],
8686
}, "--router") do
8787
# Known host without rewrites
88-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
88+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
8989
:headers => { "Host" => "default.foo" },
9090
}))
9191
assert_response_code(301, response)
9292
assert_equal("https://example.com/something/?foo=bar", response.headers["location"])
9393

9494
# Unknown host
95-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
95+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
9696
:headers => { "Host" => "unknown.foo" },
9797
}))
9898
assert_response_code(404, response)
@@ -133,18 +133,18 @@ def test_precedence
133133
})
134134

135135
# Rewrites match before API Backends.
136-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/api-example/rewrite_me", http_opts)
136+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/api-example/rewrite_me", http_opts)
137137
assert_response_code(301, response)
138138
assert_equal("https://example.com/", response.headers["location"])
139-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/api-example/rewrite_me_just_kidding", http_opts)
139+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/api-example/rewrite_me_just_kidding", http_opts)
140140
assert_response_code(200, response)
141141
assert_match("Hello World", response.body)
142142

143143
# Rewrites match before Website Backends.
144-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/website-example/rewrite_me", http_opts)
144+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/website-example/rewrite_me", http_opts)
145145
assert_response_code(301, response)
146146
assert_equal("https://2.example.com/", response.headers["location"])
147-
response = Typhoeus.get("http://127.0.0.1:9080/#{unique_test_id}/website-example/rewrite_me_just_kidding", http_opts)
147+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/website-example/rewrite_me_just_kidding", http_opts)
148148
assert_response_code(404, response)
149149
assert_match("Test 404 Not Found", response.body)
150150

0 commit comments

Comments
 (0)