twitterchiver/viewer: swap openshiftauth for pomerium

This commit is contained in:
Luke Granger-Brown 2021-03-30 21:59:18 +01:00
parent e3b60baa72
commit 3a3acc4673
10 changed files with 95 additions and 361 deletions

View file

@ -5,6 +5,5 @@
args: { args: {
twitterchiver = import ./twitterchiver args; twitterchiver = import ./twitterchiver args;
twitternuke = import ./twitternuke args; twitternuke = import ./twitternuke args;
openshiftauth = import ./openshiftauth args;
minotarproxy = import ./minotarproxy args; minotarproxy = import ./minotarproxy args;
} }

View file

@ -16,5 +16,6 @@ require (
github.com/gorilla/sessions v1.2.1 github.com/gorilla/sessions v1.2.1
github.com/jackc/pgtype v1.4.2 github.com/jackc/pgtype v1.4.2
github.com/jackc/pgx/v4 v4.8.1 github.com/jackc/pgx/v4 v4.8.1
github.com/pomerium/sdk-go v0.0.5 // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
) )

View file

@ -32,6 +32,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@ -127,6 +128,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pomerium/sdk-go v0.0.5 h1:8iJg/uZ7AmhGfHvmHmpZOswzg9MDMcmpTMX0zpmDxxs=
github.com/pomerium/sdk-go v0.0.5/go.mod h1:z4HXhKxVTIQqmffpjQP6Oi+oP9r37a0Kx/jq69jcNRo=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
@ -145,6 +148,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@ -163,6 +167,8 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -201,8 +207,10 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -242,8 +250,11 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -1,28 +0,0 @@
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
#
# SPDX-License-Identifier: Apache-2.0
{ depot, ... }: {
openshiftauth = depot.third_party.buildGo.package {
name = "hg.lukegb.com/lukegb/depot/go/openshiftauth";
srcs = [ ./openshiftauth.go ];
deps = with depot.third_party; [
gopkgs."github.com".dghubble.gologin.v2
gopkgs."github.com".dghubble.gologin.v2.oauth2
gopkgs."github.com".dgrijalva.jwt-go
gopkgs."github.com".gorilla.mux
gopkgs."github.com".gorilla.securecookie
gopkgs."github.com".gorilla.sessions
gopkgs."golang.org".x.oauth2
];
};
example = depot.third_party.buildGo.program {
name = "example";
srcs = [ ./example/example.go ];
deps = with depot.third_party; [
depot.go.openshiftauth.openshiftauth
gopkgs."github.com".gorilla.mux
];
};
}

View file

@ -1,34 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"hg.lukegb.com/lukegb/depot/go/openshiftauth"
)
func main() {
r := mux.NewRouter()
authR, err := openshiftauth.NewRouter(r)
if err != nil {
log.Fatalf("openshiftauth.NewRouter: %v", err)
}
authR.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
u := openshiftauth.UserFromContext(r.Context())
rw.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(rw)
enc.SetIndent("", " ")
enc.Encode(u)
})
r.HandleFunc("/healthz", func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(rw, "ok")
})
http.Handle("/", r)
http.ListenAndServe(":8080", nil)
}

View file

@ -1,285 +0,0 @@
// SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
//
// SPDX-License-Identifier: Apache-2.0
package openshiftauth
import (
"context"
"encoding/gob"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"github.com/dghubble/gologin/v2"
"github.com/dghubble/gologin/v2/oauth2"
"github.com/dgrijalva/jwt-go"
"github.com/gorilla/mux"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
xoauth2 "golang.org/x/oauth2"
)
type SecretGetter func(ctx context.Context) (string, error)
func TokenDirSecretGetter(ctx context.Context) (string, error) {
v, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
return "", fmt.Errorf("couldn't read serviceaccount secret: %w", err)
}
return string(v), nil
}
type BaseHandler struct {
Config xoauth2.Config
SecretGetter SecretGetter
}
func (h *BaseHandler) config(ctx context.Context) (*xoauth2.Config, error) {
s, err := h.SecretGetter(ctx)
if err != nil {
return nil, err
}
cfg := h.Config
cfg.ClientSecret = s
return &cfg, nil
}
func (h *BaseHandler) wrapWithConfig(f func(cfg *xoauth2.Config) http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
cfg, err := h.config(r.Context())
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
f(cfg).ServeHTTP(rw, r)
})
}
func (h *BaseHandler) CallbackHandler(success, failure http.Handler) http.Handler {
return h.wrapWithConfig(func(cfg *xoauth2.Config) http.Handler {
return oauth2.CallbackHandler(cfg, success, failure)
})
}
func (h *BaseHandler) LoginHandler(failure http.Handler) http.Handler {
return h.wrapWithConfig(func(cfg *xoauth2.Config) http.Handler {
return oauth2.LoginHandler(cfg, failure)
})
}
type contextKey string
const (
sessionContextKey contextKey = "session"
)
func AttachSessionMiddleware(sess sessions.Store) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s, err := sess.Get(r, "openshiftauth")
if err != nil {
log.Printf("decoding session: %v", err)
}
ctx := context.WithValue(r.Context(), sessionContextKey, s)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func Session(ctx context.Context) *sessions.Session {
return ctx.Value(sessionContextKey).(*sessions.Session)
}
func UserFromContext(ctx context.Context) *User {
s := Session(ctx)
if s.Values["user"] != nil {
return s.Values["user"].(*User)
}
return nil
}
func secretFromEnv(name string, length int) []byte {
var bs []byte
if bsHex, ok := os.LookupEnv(name); ok {
newBS, err := hex.DecodeString(bsHex)
if err != nil {
log.Printf("failed to decode %v as hex: %v", name, err)
} else {
bs = newBS
}
}
if bs == nil {
log.Printf("generating a random %v: this is bad", name)
bs = securecookie.GenerateRandomKey(length)
}
return bs
}
type User struct {
Metadata struct {
Name string `json:"name"`
} `json:"metadata"`
FullName string `json:"fullName"`
Identities []string `json:"identities"`
Groups []string `json:"groups"`
}
func init() {
gob.Register(&User{})
}
func fetchUserForToken(ctx context.Context, token *xoauth2.Token) (*User, error) {
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.k8s.lukegb.tech:6443/apis/user.openshift.io/v1/users/~", nil)
if err != nil {
return nil, fmt.Errorf("http.NewRequestWithContext for users/~ failed")
}
token.SetAuthHeader(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("executing request for users/~ failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("openshift returned non-OK status %d %v", resp.StatusCode, resp.Status)
}
var u User
if err := json.NewDecoder(resp.Body).Decode(&u); err != nil {
return nil, fmt.Errorf("decoding response from openshift: %v", err)
}
return &u, nil
}
func RequireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
u := UserFromContext(r.Context())
if u == nil {
redirURL := r.URL.ResolveReference(r.URL)
redirURL.Path = "/.login/start"
redirURL.RawQuery = url.Values{
"next": []string{r.URL.String()},
}.Encode()
http.Redirect(rw, r, redirURL.String(), http.StatusSeeOther)
return
}
next.ServeHTTP(rw, r)
})
}
type serviceAccountDetails struct {
Namespace string `json:"kubernetes.io/serviceaccount/namespace"`
Name string `json:"kubernetes.io/serviceaccount/service-account.name"`
FullName string `json:"sub"`
}
func parseServiceAccount() (*serviceAccountDetails, error) {
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
return nil, fmt.Errorf("reading service account JWT: %w", err)
}
// ParseUnverified is safe here because we're just using it to extract information about ourselves.
p := &jwt.Parser{}
claims := make(jwt.MapClaims)
if _, _, err := p.ParseUnverified(string(token), claims); err != nil {
return nil, fmt.Errorf("parsing JWT: %w", err)
}
val := func(s string) string {
v, ok := claims[s]
if !ok {
return ""
}
return v.(string)
}
sad := &serviceAccountDetails{
Namespace: val("kubernetes.io/serviceaccount/namespace"),
Name: val("kubernetes.io/serviceaccount/service-account.name"),
FullName: val("sub"),
}
return sad, nil
}
func NewRouter(r *mux.Router) (*mux.Router, error) {
sess := sessions.NewCookieStore(
secretFromEnv("OPENSHIFT_AUTH_SESSION_SIGN_SECRET", 32),
secretFromEnv("OPENSHIFT_AUTH_SESSION_ENC_SECRET", 32))
r.Use(AttachSessionMiddleware(sess))
sad, err := parseServiceAccount()
if err != nil {
return nil, fmt.Errorf("loading service account information: %w", err)
}
log.Printf("using service account token for %v", sad.FullName)
bh := &BaseHandler{
Config: xoauth2.Config{
ClientID: sad.FullName,
Endpoint: xoauth2.Endpoint{
AuthURL: "https://oauth-openshift.apps.k8s.lukegb.tech/oauth/authorize",
TokenURL: "https://oauth-openshift.apps.k8s.lukegb.tech/oauth/token",
},
RedirectURL: fmt.Sprintf("%s/.login/callback", os.Getenv("OPENSHIFT_AUTH_BASE")),
Scopes: []string{"user:info"},
},
SecretGetter: TokenDirSecretGetter,
}
baseURL, err := url.Parse(os.Getenv("OPENSHIFT_AUTH_BASE"))
if err != nil {
return nil, fmt.Errorf("failed to parse OPENSHIFT_AUTH_BASE as URL: %v", err)
}
stateConfig := gologin.DefaultCookieConfig
r.HandleFunc("/.login/start", func(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if nextURL, err := url.Parse(r.URL.Query().Get("next")); err == nil {
nextURL = baseURL.ResolveReference(nextURL)
if nextURL.Scheme != baseURL.Scheme || nextURL.User != nil || nextURL.Host != baseURL.Host {
nextURL = nil
}
if nextURL != nil {
s := Session(ctx)
s.AddFlash(nextURL.RequestURI(), "_openshiftauth_next")
sessions.Save(r, rw)
}
}
oauth2.StateHandler(stateConfig, bh.LoginHandler(nil)).ServeHTTP(rw, r)
})
r.Handle("/.login/callback", oauth2.StateHandler(stateConfig, bh.CallbackHandler(
http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
token, err := oauth2.TokenFromContext(ctx)
if err != nil {
http.Error(rw, "oauth2 TokenFromContext failed", http.StatusBadRequest)
return
}
u, err := fetchUserForToken(ctx, token)
if err != nil {
http.Error(rw, fmt.Sprintf("fetchUserForToken: %v", err), http.StatusInternalServerError)
return
}
s := Session(ctx)
s.Values["user"] = *u
sessions.Save(r, rw)
next := "/"
nexts := s.Flashes("_openshiftauth_next")
if len(nexts) >= 1 {
next = nexts[0].(string)
}
http.Redirect(rw, r, next, http.StatusFound)
}),
nil)))
authR := r.PathPrefix("/").Subrouter()
authR.Use(RequireAuth)
return authR, nil
}

View file

@ -22,7 +22,7 @@
depot.third_party.gopkgs."github.com".gorilla.mux depot.third_party.gopkgs."github.com".gorilla.mux
depot.third_party.gopkgs."github.com".jackc.pgtype depot.third_party.gopkgs."github.com".jackc.pgtype
depot.third_party.gopkgs."github.com".jackc.pgx.v4.pgxpool depot.third_party.gopkgs."github.com".jackc.pgx.v4.pgxpool
depot.go.openshiftauth.openshiftauth depot.third_party.gopkgs."github.com".pomerium.sdk-go
]; ];
dockerData = [ ( dockerData = [ (
depot.pkgs.runCommand "source" {} '' depot.pkgs.runCommand "source" {} ''

View file

@ -23,7 +23,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/jackc/pgtype" "github.com/jackc/pgtype"
"github.com/jackc/pgx/v4/pgxpool" "github.com/jackc/pgx/v4/pgxpool"
"hg.lukegb.com/lukegb/depot/go/openshiftauth" pomerium "github.com/pomerium/sdk-go"
) )
var ( var (
@ -100,6 +100,17 @@ func bestVariant(vs []interface{}) interface{} {
return bestVariant return bestVariant
} }
type mapDatastore map[interface{}]interface{}
func (ds mapDatastore) Get(key interface{}) (value interface{}, ok bool) {
value, ok = ds[key]
return value, ok
}
func (ds mapDatastore) Add(key, value interface{}) {
ds[key] = value
}
func main() { func main() {
flag.Parse() flag.Parse()
ctx := context.Background() ctx := context.Background()
@ -117,20 +128,30 @@ func main() {
}) })
var authR *mux.Router var authR *mux.Router
if *localDisableAuth != "" { var v *pomerium.Verifier
authR = r if *localDisableAuth == "" {
} else { authR = r.PathPrefix("/").Subrouter()
authR, err = openshiftauth.NewRouter(r) var err error
v, err = pomerium.New(&pomerium.Options{
JWKSEndpoint: "https://auth.int.lukegb.com/.well-known/pomerium/jwks.json",
Datastore: mapDatastore{},
})
if err != nil { if err != nil {
log.Fatalf("openshiftauth.NewRouter: %v", err) log.Fatalf("pomerium.New: %v", err)
} }
authR.Use(pomerium.AddIdentityToRequest(v))
} }
authR = r
userFromContext := func(ctx context.Context) string { userFromContext := func(ctx context.Context) (string, error) {
if *localDisableAuth != "" { if *localDisableAuth != "" {
return *localDisableAuth return *localDisableAuth, nil
} }
return openshiftauth.UserFromContext(ctx).Metadata.Name identity, err := pomerium.FromContext(ctx)
if err != nil {
return "", err
}
return identity.User, nil
} }
writeError := func(rw http.ResponseWriter, status int, wrap string, err error) { writeError := func(rw http.ResponseWriter, status int, wrap string, err error) {
@ -143,7 +164,11 @@ func main() {
authR.PathPrefix("/media/").Handler(http.StripPrefix("/media/", http.FileServer(http.Dir(*mediaDirectory)))) authR.PathPrefix("/media/").Handler(http.StripPrefix("/media/", http.FileServer(http.Dir(*mediaDirectory))))
authR.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { authR.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
user := userFromContext(ctx) user, err := userFromContext(ctx)
if err != nil {
http.Error(rw, fmt.Errorf("userFromContext: %w", err).Error(), http.StatusInternalServerError)
return
}
twitterAccounts := userMapping[user] twitterAccounts := userMapping[user]
var allTweets, timelineTweets, unfetchedMedia, unfetchedRelated int var allTweets, timelineTweets, unfetchedMedia, unfetchedRelated int
@ -214,7 +239,11 @@ ORDER BY 1
}) })
}) })
isAllowedToSee := func(ctx context.Context, twitterUser string) bool { isAllowedToSee := func(ctx context.Context, twitterUser string) bool {
twitterAccounts := userMapping[userFromContext(ctx)] user, err := userFromContext(ctx)
if err != nil {
return false
}
twitterAccounts := userMapping[user]
for _, a := range twitterAccounts { for _, a := range twitterAccounts {
if a == twitterUser { if a == twitterUser {
return true return true
@ -241,8 +270,13 @@ ORDER BY 1
ctx := r.Context() ctx := r.Context()
vars := mux.Vars(r) vars := mux.Vars(r)
twitterUser := vars["twitterUser"] twitterUser := vars["twitterUser"]
user, err := userFromContext(ctx)
if err != nil {
http.Error(rw, fmt.Errorf("userFromContext: %w", err).Error(), http.StatusInternalServerError)
return
}
if !isAllowedToSee(ctx, twitterUser) { if !isAllowedToSee(ctx, twitterUser) {
writeError(rw, http.StatusNotFound, "no such twitter user being archived", fmt.Errorf("user %q attempted to access %q", userFromContext(ctx), twitterUser)) writeError(rw, http.StatusNotFound, "no such twitter user being archived", fmt.Errorf("user %q attempted to access %q", user, twitterUser))
return return
} }

View file

@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
#
# SPDX-License-Identifier: Apache-2.0
{ depot, ... }:
depot.third_party.buildGo.external {
path = "github.com/pomerium/sdk-go";
src = depot.third_party.nixpkgs.fetchFromGitHub {
owner = "pomerium";
repo = "sdk-go";
rev = "v0.0.5";
hash = "sha256:08sa6jzixw09w519lr69wb69z7hx1158jbji9kh6f9wvxp0ny9lw";
};
deps = with depot.third_party; [
gopkgs."gopkg.in".square."go-jose.v2"
gopkgs."gopkg.in".square."go-jose.v2".jwt
];
}

View file

@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
#
# SPDX-License-Identifier: Apache-2.0
{ depot, ... }:
depot.third_party.buildGo.external {
path = "gopkg.in/square/go-jose.v2";
src = depot.third_party.nixpkgs.fetchFromGitHub {
owner = "square";
repo = "go-jose";
rev = "v2.5.1";
hash = "sha256:0z0hbmb5yyvnjkyiyn259wkbqbjxs2pzx87jz472shn2bgggxa4n";
};
deps = with depot.third_party; [
gopkgs."golang.org".x.crypto.ed25519
gopkgs."golang.org".x.crypto.pbkdf2
];
}