Skip to content

Commit

Permalink
Pull request: home: don't allow all origins
Browse files Browse the repository at this point in the history
Merge in DNS/adguard-home from 2484-access-control to master

Updates AdguardTeam#2484.

Squashed commit of the following:

commit 4f0c6dd
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Feb 5 12:42:22 2021 +0300

    home: don't allow all origins
  • Loading branch information
ainar-g authored and heyxkhoa committed Mar 17, 2023
1 parent 9c6e6bc commit dcc6a08
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 23 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ and this project adheres to

### Changed

- `Access-Control-Allow-Origin` is now only set to the same origin as the
domain, but with an HTTP scheme as opposed to `*` ([#2484]).
- `workDir` now supports symlinks.
- Stopped mounting together the directories `/opt/adguardhome/conf` and
`/opt/adguardhome/work` in our Docker images ([#2589]).
Expand All @@ -67,6 +69,7 @@ and this project adheres to
[#2358]: https://github.com/AdguardTeam/AdGuardHome/issues/2358
[#2391]: https://github.com/AdguardTeam/AdGuardHome/issues/2391
[#2394]: https://github.com/AdguardTeam/AdGuardHome/issues/2394
[#2484]: https://github.com/AdguardTeam/AdGuardHome/issues/2484
[#2509]: https://github.com/AdguardTeam/AdGuardHome/issues/2509
[#2552]: https://github.com/AdguardTeam/AdGuardHome/issues/2552
[#2589]: https://github.com/AdguardTeam/AdGuardHome/issues/2589
Expand Down
89 changes: 67 additions & 22 deletions internal/home/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package home

import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
Expand Down Expand Up @@ -202,37 +203,81 @@ func preInstallHandler(handler http.Handler) http.Handler {
return &preInstallHandlerStruct{handler}
}

// postInstall lets the handler run only if firstRun is false, and redirects to /install.html otherwise
// it also enforces HTTPS if it is enabled and configured
const defaultHTTPSPort = 443

// handleHTTPSRedirect redirects the request to HTTPS, if needed. If ok is
// true, the middleware must continue handling the request.
func handleHTTPSRedirect(w http.ResponseWriter, r *http.Request) (ok bool) {
web := Context.web
if web.httpsServer.server == nil {
return true
}

host, _, err := net.SplitHostPort(r.Host)
if err != nil {
// Check for the missing port error. If it is that error, just
// use the host as is.
//
// See the source code for net.SplitHostPort.
const missingPort = "missing port in address"

addrErr := &net.AddrError{}
if !errors.As(err, &addrErr) || addrErr.Err != missingPort {
httpError(w, http.StatusBadRequest, "bad host: %s", err)

return false
}

host = r.Host
}

if r.TLS == nil && web.forceHTTPS {
hostPort := host
if port := web.conf.PortHTTPS; port != defaultHTTPSPort {
portStr := strconv.Itoa(port)
hostPort = net.JoinHostPort(host, portStr)
}

httpsURL := &url.URL{
Scheme: "https",
Host: hostPort,
Path: r.URL.Path,
RawQuery: r.URL.RawQuery,
}
http.Redirect(w, r, httpsURL.String(), http.StatusTemporaryRedirect)

return false
}

// Allow the frontend from the HTTP origin to send requests to the HTTPS
// server. This can happen when the user has just set up HTTPS with
// redirects.
originURL := &url.URL{
Scheme: "http",
Host: r.Host,
}
w.Header().Set("Access-Control-Allow-Origin", originURL.String())

return true
}

// postInstall lets the handler to run only if firstRun is false. Otherwise, it
// redirects to /install.html. It also enforces HTTPS if it is enabled and
// configured and sets appropriate access control headers.
func postInstall(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if Context.firstRun &&
!strings.HasPrefix(r.URL.Path, "/install.") &&
!strings.HasPrefix(r.URL.Path, "/assets/") {
path := r.URL.Path
if Context.firstRun && !strings.HasPrefix(path, "/install.") &&
!strings.HasPrefix(path, "/assets/") {
http.Redirect(w, r, "/install.html", http.StatusFound)

return
}

// enforce https?
if r.TLS == nil && Context.web.forceHTTPS && Context.web.httpsServer.server != nil {
// yes, and we want host from host:port
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
// no port in host
host = r.Host
}
// construct new URL to redirect to
newURL := url.URL{
Scheme: "https",
Host: net.JoinHostPort(host, strconv.Itoa(Context.web.conf.PortHTTPS)),
Path: r.URL.Path,
RawQuery: r.URL.RawQuery,
}
http.Redirect(w, r, newURL.String(), http.StatusTemporaryRedirect)
if !handleHTTPSRedirect(w, r) {
return
}

w.Header().Set("Access-Control-Allow-Origin", "*")
handler(w, r)
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/home/home_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func TestHome(t *testing.T) {
h := http.Client{}
for i := 0; i != 50; i++ {
resp, err = h.Get("http://127.0.0.1:3000/")
if err == nil && resp.StatusCode != 404 {
if err == nil && resp.StatusCode != http.StatusNotFound {
break
}
time.Sleep(100 * time.Millisecond)
Expand Down

0 comments on commit dcc6a08

Please sign in to comment.