Skip to content

Commit 40cd977

Browse files
committed
initial commit
0 parents  commit 40cd977

File tree

6 files changed

+331
-0
lines changed

6 files changed

+331
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
config.yml
2+
gate

conf.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"gopkg.in/yaml.v1"
6+
"io/ioutil"
7+
)
8+
9+
type Conf struct {
10+
Addr string `yaml:"address"`
11+
SSL SSLConf `yaml:"ssl"`
12+
Auth AuthConf `yaml:"auth"`
13+
Domain string `yaml:"domain"`
14+
Proxies []ProxyConf `yaml:"proxy"`
15+
Htdocs string `yaml:"htdocs"`
16+
}
17+
18+
type SSLConf struct {
19+
Cert string `yaml:"cert"`
20+
Key string `yaml:"key"`
21+
}
22+
23+
type AuthConf struct {
24+
Session AuthSessionConf `yaml:"session"`
25+
Google AuthGoogleConf `yaml:"google"`
26+
}
27+
28+
type AuthSessionConf struct {
29+
Key string `yaml:"key"`
30+
}
31+
32+
type AuthGoogleConf struct {
33+
ClientId string `yaml:"client_id"`
34+
ClientSecret string `yaml:"client_secret"`
35+
RedirectURL string `yaml:"redirect_url"`
36+
}
37+
38+
type ProxyConf struct {
39+
Path string `yaml:"path"`
40+
Dest string `yaml:"dest"`
41+
Strip bool `yaml:"strip_path"`
42+
}
43+
44+
func ParseConf(path string) (*Conf, error) {
45+
data, err := ioutil.ReadFile(path)
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
c := &Conf{}
51+
if err := yaml.Unmarshal(data, c); err != nil {
52+
return nil, err
53+
}
54+
55+
if c.Addr == "" {
56+
return nil, errors.New("address config is required")
57+
}
58+
59+
if c.Auth.Session.Key == "" {
60+
return nil, errors.New("auth.session.key config is required")
61+
}
62+
if c.Auth.Google.ClientId == "" {
63+
return nil, errors.New("auth.google.client_id config is required")
64+
}
65+
if c.Auth.Google.ClientSecret == "" {
66+
return nil, errors.New("auth.google.client_secret config is required")
67+
}
68+
if c.Auth.Google.RedirectURL == "" {
69+
return nil, errors.New("auth.google.redirect_url config is required")
70+
}
71+
72+
if c.Htdocs == "" {
73+
c.Htdocs = "."
74+
}
75+
76+
return c, nil
77+
}

config_sample.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# address to bind
2+
address: :9999
3+
4+
# # ssl keys (optional)
5+
# ssl:
6+
# cert: ./ssl/ssl.cer
7+
# key: ./ssl/ssl.key
8+
9+
auth:
10+
session:
11+
# authentication key for cookie store
12+
key: secret123
13+
14+
google:
15+
# your google app keys
16+
client_id: your client id
17+
client_secret: your client secret
18+
# your google app redirect_url: path is always "/oauth2callback"
19+
redirect_url: https://yourapp.example.com/oauth2callback
20+
21+
# # restrict domain. (optional)
22+
# domain: yourdomain.com
23+
24+
# document root for static files
25+
htdocs: ./
26+
27+
# proxy definitions
28+
proxy:
29+
- path: /elasticsearch
30+
dest: http://127.0.0.1:9200
31+
strip_path: yes
32+
33+
- path: /influxdb
34+
dest: http://127.0.0.1:8086
35+
strip_path: yes

config_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package main
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"testing"
7+
"fmt"
8+
)
9+
10+
func TestParse(t *testing.T) {
11+
f, err := ioutil.TempFile("", "")
12+
if err != nil {
13+
t.Error(err)
14+
}
15+
defer os.Remove(f.Name())
16+
17+
data := `---
18+
address: ":9999"
19+
20+
htdocs: ./
21+
22+
proxy:
23+
- path: /foo
24+
dest: http://example.com/bar
25+
strip_path: yes
26+
`
27+
if err := ioutil.WriteFile(f.Name(), []byte(data), 0644); err != nil {
28+
t.Error(err)
29+
}
30+
31+
conf, err := ParseConf(f.Name())
32+
if err != nil {
33+
t.Error(err)
34+
}
35+
36+
if conf.Addr != ":9999" {
37+
t.Errorf("unexpected address: %s", conf.Addr)
38+
}
39+
40+
fmt.Printf("conf: %+v\n", conf)
41+
}

httpd.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"net/http"
6+
"net/http/httputil"
7+
"net/url"
8+
"strings"
9+
10+
"encoding/base64"
11+
"encoding/json"
12+
13+
"github.com/go-martini/martini"
14+
"github.com/martini-contrib/oauth2"
15+
"github.com/martini-contrib/sessions"
16+
"path/filepath"
17+
)
18+
19+
type Server struct {
20+
Conf *Conf
21+
}
22+
23+
type User struct {
24+
Email string
25+
}
26+
27+
func NewServer(conf *Conf) *Server {
28+
return &Server{conf}
29+
}
30+
31+
func (s *Server) Run() error {
32+
m := martini.Classic()
33+
34+
m.Use(sessions.Sessions("session", sessions.NewCookieStore([]byte(s.Conf.Auth.Session.Key))))
35+
m.Use(oauth2.Google(&oauth2.Options{
36+
ClientId: s.Conf.Auth.Google.ClientId,
37+
ClientSecret: s.Conf.Auth.Google.ClientSecret,
38+
RedirectURL: s.Conf.Auth.Google.RedirectURL,
39+
Scopes: []string{"email"},
40+
}))
41+
42+
m.Use(oauth2.LoginRequired)
43+
m.Use(restrictDomain(s.Conf.Domain))
44+
45+
for i := range s.Conf.Proxies {
46+
p := s.Conf.Proxies[i]
47+
48+
if strings.HasSuffix(p.Path, "/") == false {
49+
p.Path += "/"
50+
}
51+
strip_path := p.Path
52+
53+
if strings.HasSuffix(p.Path, "**") == false {
54+
p.Path += "**"
55+
}
56+
57+
u, err := url.Parse(p.Dest)
58+
if err != nil {
59+
return err
60+
}
61+
62+
proxy := httputil.NewSingleHostReverseProxy(u)
63+
if p.Strip {
64+
m.Any(p.Path, http.StripPrefix(strip_path, proxy))
65+
} else {
66+
m.Any(p.Path, proxy)
67+
}
68+
69+
log.Printf("register proxy path:%s dest:%s", strip_path, u.String())
70+
}
71+
72+
path, err := filepath.Abs(s.Conf.Htdocs)
73+
if err != nil {
74+
return err
75+
}
76+
77+
log.Printf("starting static file server for: %s", path)
78+
fileServer := http.FileServer(http.Dir(path))
79+
m.Get("/**", fileServer.ServeHTTP)
80+
81+
log.Printf("starting server at %s", s.Conf.Addr)
82+
83+
if s.Conf.SSL.Cert != "" && s.Conf.SSL.Key != "" {
84+
return http.ListenAndServeTLS(s.Conf.Addr, s.Conf.SSL.Cert, s.Conf.SSL.Key, m)
85+
} else {
86+
return http.ListenAndServe(s.Conf.Addr, m)
87+
}
88+
}
89+
90+
func forbidden(w http.ResponseWriter) {
91+
w.WriteHeader(403)
92+
w.Write([]byte("Access denied"))
93+
}
94+
95+
// base64Decode decodes the Base64url encoded string
96+
//
97+
// steel from code.google.com/p/goauth2/oauth/jwt
98+
func base64Decode(s string) ([]byte, error) {
99+
// add back missing padding
100+
switch len(s) % 4 {
101+
case 2:
102+
s += "=="
103+
case 3:
104+
s += "="
105+
}
106+
return base64.URLEncoding.DecodeString(s)
107+
}
108+
109+
func restrictDomain(domain string) martini.Handler {
110+
return func(c martini.Context, tokens oauth2.Tokens, w http.ResponseWriter) {
111+
extra := tokens.ExtraData()
112+
if _, ok := extra["id_token"]; ok == false {
113+
log.Printf("id_token not found")
114+
forbidden(w)
115+
return
116+
}
117+
118+
keys := strings.Split(extra["id_token"], ".")
119+
if len(keys) < 2 {
120+
log.Printf("invalid id_token")
121+
forbidden(w)
122+
return
123+
}
124+
125+
data, err := base64Decode(keys[1])
126+
if err != nil {
127+
log.Printf("failed to decode base64: %s", err.Error())
128+
forbidden(w)
129+
return
130+
}
131+
132+
var info map[string]interface{}
133+
if err := json.Unmarshal(data, &info); err != nil {
134+
log.Printf("failed to decode json: %s", err.Error())
135+
forbidden(w)
136+
return
137+
}
138+
139+
if email, ok := info["email"].(string); ok {
140+
if domain == "" || strings.HasSuffix(email, "@"+domain) {
141+
user := &User{email}
142+
c.Map(user)
143+
} else {
144+
log.Printf("email doesn't allow: %s", email)
145+
forbidden(w)
146+
return
147+
}
148+
} else {
149+
log.Printf("email not found")
150+
forbidden(w)
151+
return
152+
}
153+
}
154+
}

main.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"log"
6+
)
7+
8+
var (
9+
confFile = flag.String("conf", "config.yml", "config file path")
10+
)
11+
12+
func main() {
13+
flag.Parse()
14+
15+
conf, err := ParseConf(*confFile)
16+
if err != nil {
17+
panic(err)
18+
}
19+
20+
server := NewServer(conf)
21+
log.Fatal(server.Run())
22+
}

0 commit comments

Comments
 (0)