123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- package security
- import (
- "errors"
- "fmt"
- "net"
- "net/http"
- "strings"
- "github.com/seaweedfs/seaweedfs/weed/glog"
- )
- var (
- ErrUnauthorized = errors.New("unauthorized token")
- )
- /*
- Guard is to ensure data access security.
- There are 2 ways to check access:
- 1. white list. It's checking request ip address.
- 2. JSON Web Token(JWT) generated from secretKey.
- The jwt can come from:
- 1. url parameter jwt=...
- 2. request header "Authorization"
- 3. cookie with the name "jwt"
- The white list is checked first because it is easy.
- Then the JWT is checked.
- The Guard will also check these claims if provided:
- 1. "exp" Expiration Time
- 2. "nbf" Not Before
- Generating JWT:
- 1. use HS256 to sign
- 2. optionally set "exp", "nbf" fields, in Unix time,
- the number of seconds elapsed since January 1, 1970 UTC.
- Referenced:
- https://github.com/pkieltyka/jwtauth/blob/master/jwtauth.go
- */
- type Guard struct {
- whiteList []string
- SigningKey SigningKey
- ExpiresAfterSec int
- ReadSigningKey SigningKey
- ReadExpiresAfterSec int
- isWriteActive bool
- }
- func NewGuard(whiteList []string, signingKey string, expiresAfterSec int, readSigningKey string, readExpiresAfterSec int) *Guard {
- g := &Guard{
- whiteList: whiteList,
- SigningKey: SigningKey(signingKey),
- ExpiresAfterSec: expiresAfterSec,
- ReadSigningKey: SigningKey(readSigningKey),
- ReadExpiresAfterSec: readExpiresAfterSec,
- }
- g.isWriteActive = len(g.whiteList) != 0 || len(g.SigningKey) != 0
- return g
- }
- func (g *Guard) WhiteList(f http.HandlerFunc) http.HandlerFunc {
- if !g.isWriteActive {
- //if no security needed, just skip all checking
- return f
- }
- return func(w http.ResponseWriter, r *http.Request) {
- if err := g.checkWhiteList(w, r); err != nil {
- w.WriteHeader(http.StatusUnauthorized)
- return
- }
- f(w, r)
- }
- }
- func GetActualRemoteHost(r *http.Request) (host string, err error) {
- host = r.Header.Get("HTTP_X_FORWARDED_FOR")
- if host == "" {
- host = r.Header.Get("X-FORWARDED-FOR")
- }
- if strings.Contains(host, ",") {
- host = host[0:strings.Index(host, ",")]
- }
- if host == "" {
- host, _, err = net.SplitHostPort(r.RemoteAddr)
- }
- return
- }
- func (g *Guard) checkWhiteList(w http.ResponseWriter, r *http.Request) error {
- if len(g.whiteList) == 0 {
- return nil
- }
- host, err := GetActualRemoteHost(r)
- if err == nil {
- for _, ip := range g.whiteList {
- // If the whitelist entry contains a "/" it
- // is a CIDR range, and we should check the
- // remote host is within it
- if strings.Contains(ip, "/") {
- _, cidrnet, err := net.ParseCIDR(ip)
- if err != nil {
- panic(err)
- }
- remote := net.ParseIP(host)
- if cidrnet.Contains(remote) {
- return nil
- }
- }
- //
- // Otherwise we're looking for a literal match.
- //
- if ip == host {
- return nil
- }
- }
- }
- glog.V(0).Infof("Not in whitelist: %s", r.RemoteAddr)
- return fmt.Errorf("Not in whitelist: %s", r.RemoteAddr)
- }
|