-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathkroxy.go
More file actions
128 lines (111 loc) · 3.25 KB
/
kroxy.go
File metadata and controls
128 lines (111 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import (
"errors"
"os"
"strings"
"crypto/tls"
"encoding/base64"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"github.com/Sirupsen/logrus"
"github.com/dgrijalva/jwt-go"
)
const (
SECRET_FILE = "/.secret/idp-secret"
)
// TODO:
// - add better logging and error handling
// - house keeping
func main() {
var server1Url *url.URL
var err error
if server1Url, err = url.Parse(os.Getenv("KROXY_K8S_API")); err != nil {
logrus.Fatal("Failed to parse url: ", err)
}
reverseProxy := modifiedSingleHost(server1Url)
//disable tls for now when connecting to the actual kubernetes api server
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
server := prepareServer(reverseProxy)
logrus.Info("Starting kroxy.. A simple reverse proxy that forwards request to kubernetes api")
logrus.Info("Listening on port: :8080")
err = http.ListenAndServe(":8080", server)
if err != nil {
logrus.Fatal("ListenAndServe: ", err)
}
}
func getToken() string {
token, _ := ioutil.ReadFile(os.Getenv("KROXY_TOKEN_FILE"))
return string(token)
}
func getSecret() string {
secret, _ := ioutil.ReadFile(SECRET_FILE)
return string(secret)
}
// copied from net/http/httputil reverse proxy
func modifiedSingleHost(target *url.URL) *httputil.ReverseProxy {
targetQuery := target.RawQuery
director := func(req *http.Request) {
req.URL.Scheme = target.Scheme
req.URL.Host = target.Host
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
req.Header.Set("Authorization", "Bearer "+getToken())
if targetQuery == "" || req.URL.RawQuery == "" {
req.URL.RawQuery = targetQuery + req.URL.RawQuery
} else {
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
}
}
return &httputil.ReverseProxy{Director: director}
}
// setup cors and validate id_token
func prepareServer(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "authorization, content-type")
w.Header().Set("Access-Control-Allow-Methods", "*")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
} else {
// err := validateToken(r)
// if err != nil {
// logrus.Error(err)
// w.WriteHeader(http.StatusUnauthorized)
// } else {
handler.ServeHTTP(w, r)
// }
}
})
}
func validateToken(r *http.Request) error {
var id_token string
if auth := r.Header.Get("Authorization"); auth != "" {
if len(auth) > 6 && strings.ToUpper(auth[0:7]) == "BEARER " {
id_token = strings.TrimSpace(auth[7:])
}
} else {
return errors.New("Missing Authorization Header.")
}
decoded_secret, err := base64.URLEncoding.DecodeString(getSecret())
if err != nil {
return err
}
vid_token, err := jwt.Parse(id_token, func(token *jwt.Token) (interface{}, error) { return []byte(decoded_secret), nil })
if err == nil && vid_token.Valid {
return nil
}
return errors.New("Invalid Token.")
}
// copied from net/http/httputil reverse proxy
func singleJoiningSlash(a, b string) string {
aslash := strings.HasSuffix(a, "/")
bslash := strings.HasPrefix(b, "/")
switch {
case aslash && bslash:
return a + b[1:]
case !aslash && !bslash:
return a + "/" + b
}
return a + b
}