auth-proxy: verify group membership (#105)
* auth-proxy: verify group membership
* memberships: install memberships app and use it in few apps
* app-repo: render auth
* installer: always use external dependencies option in app configs
* installer: fix auth handling
* auth-proxy: configure membership-addr and groups flags in helm chart
* installer: fix indentation
* app-manager: fix how auth block is rendered
---------
Co-authored-by: Giorgi Lekveishvili <lekva@gl-mbp-m1-max.local>
diff --git a/core/auth/proxy/Makefile b/core/auth/proxy/Makefile
index 053ab05..4ec89b0 100644
--- a/core/auth/proxy/Makefile
+++ b/core/auth/proxy/Makefile
@@ -8,21 +8,21 @@
rm -f server server_*
build: clean
- go build -o server *.go
+ /usr/local/go/bin/go build -o server *.go
build_arm64: export CGO_ENABLED=0
build_arm64: export GO111MODULE=on
build_arm64: export GOOS=linux
build_arm64: export GOARCH=arm64
build_arm64:
- go build -o server_arm64 *.go
+ /usr/local/go/bin/go build -o server_arm64 *.go
build_amd64: export CGO_ENABLED=0
build_amd64: export GO111MODULE=on
build_amd64: export GOOS=linux
build_amd64: export GOARCH=amd64
build_amd64:
- go build -o server_amd64 *.go
+ /usr/local/go/bin/go build -o server_amd64 *.go
push_arm64: clean build_arm64
$(podman) build --platform linux/arm64 --tag=$(repo_name)/auth-proxy:arm64 .
diff --git a/core/auth/proxy/go.mod b/core/auth/proxy/go.mod
new file mode 100644
index 0000000..856b8bf
--- /dev/null
+++ b/core/auth/proxy/go.mod
@@ -0,0 +1,5 @@
+module github.com/giolekva/pcloud/core/auth/proxy
+
+go 1.21.5
+
+require golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81
diff --git a/core/auth/proxy/go.sum b/core/auth/proxy/go.sum
new file mode 100644
index 0000000..76a41df
--- /dev/null
+++ b/core/auth/proxy/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc=
+golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
diff --git a/core/auth/proxy/main.go b/core/auth/proxy/main.go
index 8b3d837..d1e1b49 100644
--- a/core/auth/proxy/main.go
+++ b/core/auth/proxy/main.go
@@ -13,11 +13,15 @@
"net/http/cookiejar"
"net/url"
"strings"
+
+ "golang.org/x/exp/slices"
)
var port = flag.Int("port", 3000, "Port to listen on")
var whoAmIAddr = flag.String("whoami-addr", "", "Kratos whoami endpoint address")
var loginAddr = flag.String("login-addr", "", "Login page address")
+var membershipAddr = flag.String("membership-addr", "", "Group membership API endpoint")
+var groups = flag.String("groups", "", "Comma separated list of groups. User must be part of at least one of them. If empty group membership will not be checked.")
var upstream = flag.String("upstream", "", "Upstream service address")
type user struct {
@@ -62,6 +66,25 @@
http.Redirect(w, r, addr, http.StatusSeeOther)
return
}
+ if *groups != "" {
+ hasPermission := false
+ tg, err := getTransitiveGroups(user.Identity.Traits.Username)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ for _, i := range strings.Split(*groups, ",") {
+ if slices.Contains(tg, strings.TrimSpace(i)) {
+ hasPermission = true
+ break
+ }
+ }
+ if !hasPermission {
+ http.Error(w, "not authorized", http.StatusUnauthorized)
+ return
+ }
+
+ }
rc := r.Clone(context.Background())
rc.Header.Add("X-User", user.Identity.Traits.Username)
ru, err := url.Parse(fmt.Sprintf("http://%s%s", *upstream, r.URL.RequestURI()))
@@ -148,8 +171,27 @@
return nil, fmt.Errorf("Unknown error: %s", tmp)
}
+type MembershipInfo struct {
+ MemberOf []string `json:"memberOf"`
+}
+
+func getTransitiveGroups(user string) ([]string, error) {
+ resp, err := http.Get(fmt.Sprintf("%s/%s", *membershipAddr, user))
+ if err != nil {
+ return nil, err
+ }
+ var info MembershipInfo
+ if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
+ return nil, err
+ }
+ return info.MemberOf, nil
+}
+
func main() {
flag.Parse()
+ if *groups != "" && *membershipAddr == "" {
+ log.Fatal("membership-addr flag is required when groups are provided")
+ }
http.HandleFunc("/", handle)
fmt.Printf("Starting HTTP server on port: %d\n", *port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))