Initial Commit

main
James Mills 2 weeks ago
parent 3902a0f12c
commit 3cc59fb21c
Signed by: prologic
GPG Key ID: AC4C014F1440EBD6
  1. 1
      .gitignore
  2. 12
      .goreleaser.yml
  3. 12
      Dockerfile
  4. 44
      README.md
  5. 5
      assets/authn.yaml
  6. BIN
      assets/logo-small.png
  7. BIN
      assets/logo.png
  8. 4
      authn.yaml
  9. 8
      go.mod
  10. 6
      go.sum
  11. 79
      main.go
  12. 42
      proxy/auth.go
  13. 32
      proxy/config.go
  14. 23
      proxy/reverse_proxy.go

1
.gitignore vendored

@ -0,0 +1 @@
basic-auth-reverse-proxy

@ -0,0 +1,12 @@
before:
hooks:
- go mod download
builds:
- env:
- CGO_ENABLED=0
- GO111MODULE=on
dockers:
- image_templates:
- angelbarrera92/{{.ProjectName}}:latest
- angelbarrera92/{{.ProjectName}}:v{{ .Major }}
- angelbarrera92/{{.ProjectName}}:{{ .Tag }}

@ -0,0 +1,12 @@
FROM alpine as alpine
RUN apk add -U --no-cache ca-certificates
FROM scratch
WORKDIR /
COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY basic-auth-reverse-proxy /basic-auth-reverse-proxy
ENTRYPOINT [ "/basic-auth-reverse-proxy" ]

@ -1,3 +1,43 @@
# basic-auth-reverse-proxy
# Basic Auth Reverse Proxy
Fork of https://github.com/angelbarrera92/basic-auth-reverse-proxy
This project offers a way to securize your backends with a basic golang reverse proxy with basic auth configuration.
![Logo](assets/logo-small.png)
## TLDR;
Expose your services with basic authentication.
```bash
$ wget -O basic-auth-reverse-proxy.tar.gz -q https://github.com/angelbarrera92/basic-auth-reverse-proxy/releases/download/v0.1.2/basic-auth-reverse-proxy_0.1.2_linux_amd64.tar.gz
$ tar -zxvf basic-auth-reverse-proxy.tar.gz
README.md
basic-auth-reverse-proxy
$ cat >> authn.yaml <<EOL
users:
- username: Angel
password: Barrera
EOL
$ ./basic-auth-reverse-proxy serve
```
Then a local server is started. Try to access it:
```bash
$ curl http://localhost:11811/get
Unauthorised
$ curl http://Angel:Barrera@localhost:11811/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip",
"Authorization": "Basic QW5nZWw6QmFycmVyYQ==",
"Host": "httpbin.org",
"User-Agent": "curl/7.58.0",
"X-Forwarded-Host": "localhost:11811"
},
"origin": "127.0.0.1, 80.25.227.133, 127.0.0.1",
"url": "https://localhost:11811/get"
}
```

@ -0,0 +1,5 @@
users:
- username: Angel
password: Barrera
- username: Pepe
password: Gotera

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

@ -0,0 +1,4 @@
---
users:
- username: admin
password: admin

@ -0,0 +1,8 @@
module github.com/angelbarrera92/basic-auth-reverse-proxy
go 1.12
require (
gopkg.in/urfave/cli.v1 v1.20.0
gopkg.in/yaml.v2 v2.2.2
)

@ -0,0 +1,6 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

@ -0,0 +1,79 @@
package main
import (
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"github.com/angelbarrera92/basic-auth-reverse-proxy/proxy"
"gopkg.in/urfave/cli.v1"
)
var (
version = "dev"
commit = "none"
date = "unknown"
)
func serve(c *cli.Context) error {
upstream := c.String("upstream")
port := c.Int("port")
authConfigPath := c.String("auth-config")
realm := c.String("realm")
authConfig, err := proxy.ParseConfig(&authConfigPath)
if err != nil {
log.Fatalf("Can not read auth configuration file: %v", err)
return err
}
upstreamURL, _ := url.Parse(upstream)
reverseProxy := httputil.NewSingleHostReverseProxy(upstreamURL)
http.HandleFunc("/", proxy.BasicAuth(proxy.ReverseProxyHandler(reverseProxy, upstreamURL), *authConfig, realm))
serveAt := fmt.Sprintf(":%d", port)
if err := http.ListenAndServe(serveAt, nil); err != nil {
log.Fatalf("Reverse Proxy can not start %v", err)
return err
}
return nil
}
func main() {
app := cli.NewApp()
app.Name = "Basic Auth Reverse Proxy"
app.Usage = "Makes your upstream service secure"
app.Version = version
app.Author = "Ángel Barrera - @angelbarrera92"
app.Commands = []cli.Command{
{
Name: "serve",
Usage: "Runs the reverse proxy",
Action: serve,
Flags: []cli.Flag{
cli.IntFlag{
Name: "port",
Usage: "Port used to expose this reverse proxy",
Value: 11811,
}, cli.StringFlag{
Name: "upstream",
Usage: "Upstream server. Server that will be protected by this reverse proxy",
Value: "https://httpbin.org",
}, cli.StringFlag{
Name: "realm",
Usage: "Reverse proxy realm",
Value: "My Reverse Proxy",
}, cli.StringFlag{
Name: "auth-config",
Usage: "AuthN yaml configuration file path",
Value: "authn.yaml",
},
},
},
}
app.Run(os.Args)
}

@ -0,0 +1,42 @@
package proxy
import (
"crypto/subtle"
"net/http"
)
// BasicAuth Protects a handler with a users authn backend
func BasicAuth(handler http.HandlerFunc, users Authn, realm string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
if !ok {
writeUnauthorisedResponse(w, realm)
return
}
authn := false
for _, v := range users.Users {
if subtle.ConstantTimeCompare([]byte(user), []byte(v.Username)) == 1 && subtle.ConstantTimeCompare([]byte(pass), []byte(v.Password)) == 1 {
authn = true
break
}
}
if !authn {
writeUnauthorisedResponse(w, realm)
return
}
r.Header.Set("X-User", user)
handler(w, r)
}
}
func writeUnauthorisedResponse(w http.ResponseWriter, realm string) {
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
w.WriteHeader(401)
w.Write([]byte("Unauthorised\n"))
}

@ -0,0 +1,32 @@
package proxy
import (
"io/ioutil"
"gopkg.in/yaml.v2"
)
// Authn Contains a list of users
type Authn struct {
Users []User `yaml:"users"`
}
// User Identifies an username
type User struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
}
// ParseConfig read a configuration file in the path `location` and returns an Authn object
func ParseConfig(location *string) (*Authn, error) {
data, err := ioutil.ReadFile(*location)
if err != nil {
return nil, err
}
authn := Authn{}
err = yaml.Unmarshal([]byte(data), &authn)
if err != nil {
return nil, err
}
return &authn, nil
}

@ -0,0 +1,23 @@
package proxy
import (
"net/http"
"net/http/httputil"
"net/url"
)
func modifyRequest(r *http.Request, upstreamURL *url.URL) {
// Update the headers to allow for SSL redirection
r.URL.Host = upstreamURL.Host
r.URL.Scheme = upstreamURL.Scheme
r.Header.Set("X-Forwarded-Host", r.Host)
r.Host = upstreamURL.Host
}
// ReverseProxyHandler Handle every proxt request
func ReverseProxyHandler(reverseProxy *httputil.ReverseProxy, upstreamURL *url.URL) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
modifyRequest(r, upstreamURL)
reverseProxy.ServeHTTP(w, r)
}
}
Loading…
Cancel
Save