diff --git a/.gitignore b/.gitignore index 4c44883..4d7d64d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -vendor -panelcli -userpanel +/vendor +/panelcli +/userpanel diff --git a/Dockerfile b/Dockerfile index a7a873a..607a183 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM golang:1.11-alpine as builder RUN apk --no-cache add git gcc musl-dev WORKDIR /go/src/git.lattuga.net/boyska/feedpanel/ COPY ./ /go/src/git.lattuga.net/boyska/feedpanel/ -RUN CGO_ENABLED=0 GOOS=linux go install -v ./... +RUN CGO_ENABLED=0 GOOS=linux go install ./... FROM busybox WORKDIR /usr/bin/ diff --git a/cmd/userpanel/main.go b/cmd/userpanel/main.go index ce7b69d..40b9a1d 100644 --- a/cmd/userpanel/main.go +++ b/cmd/userpanel/main.go @@ -3,13 +3,25 @@ package main import ( "fmt" "net/http" + + "github.com/urfave/negroni" ) func main() { - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(501) - fmt.Fprintf(w, "Nothing has been implemented. Just cross your fingers.\n") + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + user := r.Context().Value(keyUser) + if user == nil { + panic("Authentication middleware failed!") + } + w.WriteHeader(200) + fmt.Fprintf(w, "Hello, %s\n", user) }) - http.ListenAndServe(":8000", nil) + ha := HeaderAuth{AllowedNames: []string{"feedati-fe"}, RequireUser: true} + n := negroni.New(negroni.NewRecovery(), negroni.NewLogger(), ha) + n.UseHandler(mux) + addr := ":8000" + fmt.Println("Listening on", addr) + http.ListenAndServe(addr, n) } diff --git a/cmd/userpanel/middleware.go b/cmd/userpanel/middleware.go new file mode 100644 index 0000000..2273e3c --- /dev/null +++ b/cmd/userpanel/middleware.go @@ -0,0 +1,76 @@ +package main + +import ( + "context" + "net" + "net/http" +) + +type ctxKey int + +const ( + keyUser = iota +) + +//HeaderAuth is a Negroni-compatible Middleware +type HeaderAuth struct { + AllowedNames []string + AllowedIPs []net.IP + RequireUser bool +} + +func _getSourceIP(req *http.Request) (net.IP, error) { + ip, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + return net.IP{}, err + } + + userIP := net.ParseIP(ip) + if userIP == nil { + return net.IP{}, err + } + return userIP, nil +} + +func (ha HeaderAuth) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + srcIP, err := _getSourceIP(r) + if err != nil { + rw.WriteHeader(501) + return + } + found := false + if len(ha.AllowedNames) == 0 && len(ha.AllowedIPs) == 0 { + found = true + } + if !found && len(ha.AllowedIPs) > 0 { + for _, allowedIP := range ha.AllowedIPs { + if allowedIP.Equal(srcIP) { + found = true + break + } + } + } + if !found && len(ha.AllowedNames) > 0 { + for _, name := range ha.AllowedNames { + nameips, err := net.LookupIP(name) + if err != nil { + continue + } + for _, allowedIP := range nameips { + if allowedIP.Equal(srcIP) { + found = true + break + } + } + } + } + user := r.Header.Get("X-Forwarded-User") + if !found || (ha.RequireUser && user == "") { + rw.WriteHeader(http.StatusUnauthorized) + return + } + ctx := r.Context() + ctx = context.WithValue(ctx, keyUser, user) + next(rw, r.WithContext(ctx)) + return +} diff --git a/go.mod b/go.mod index e5cc2b0..ff26104 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/onsi/gomega v1.4.2 // indirect github.com/pkg/errors v0.8.0 + github.com/urfave/negroni v1.0.0 github.com/vmihailenco/sasl v0.0.0-20180913092844-58bfd2104008 // indirect golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect diff --git a/go.sum b/go.sum index 0fb4c9b..ce7a66f 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/vmihailenco/sasl v0.0.0-20180913092844-58bfd2104008 h1:QvUVTr/4BCSyk46CIcYxDh5qQJbV65nWcMlv6whzm1Y= github.com/vmihailenco/sasl v0.0.0-20180913092844-58bfd2104008/go.mod h1:PL3B2VvrDEUUKvagXPKVvvmulVvRE2DWIJovqpdpHwI= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=