ORY Oathkeeper是一个认证与访问代理,用于作为反向代理或者访问权限决策点。
当作为反向代理时,客户端、oathkeeper以及内网服务之间的通信流程如图1所示。
图1 作为反向代理时的通信流程
当作为权限决策点时,客户端、API网关、Oathkeeper以及内网服务之间的通信流程如下如下。
图2 当作为访问决策点时的通信流程
当Oathkeeper接收到HTTP请求后,会首先对HTTP请求进行认证以及鉴权,如果全都通过,才会将HTTP请求转发到服务端。
图3 Oathkeeper主要运行流程
由上图可知,Oathkeeper主要完成对HTTP请求的认证、鉴权的任务。然而,在不与第三方认证、鉴权服务通信的前提下, oathkeeper的只有有限的认证、鉴权能力,详细如下:
oathkeeper不需要第三方时的认证、鉴权能力范围 能力范围认证鉴权更改
1.noop(全部认证通过)
2.unauthorized(全部拒绝认证)
3.Annymous(不带Authorization头部的请求可以认证通过,如果数据的访问者没有定义,则被设置为“匿名”)
1.allow(权限认证时全部通过)
2.deny(权限认证时全部失败)
尚不清楚应用场景因此,如果想要提供更全面、更完善的安全机制,只能在系统中继续搭建其他服务。在这个博客中,oathkeeper会和go-oauth2相配合,搭建安全防护系统,保护内网的一个nginx服务器。
详见Oathkeeper安装,由于网络原因,有些依赖包可能无法下载,可以通过设置代理或者手动下载完成,注意的是手动下载时需要将数据包安装到GOPATH下的指定路径。
核心代码如下:
/* Step1:创建admin,zhanglei两个用户,并存储在内存数据库中 */ clientStore := store.NewClientStore() root_id, root_secret := "admin", "admin_haha" root_index := root_id root_client_info := models.Client{ID: root_id, Secret: root_secret, Domain: "http://ip_of_web:80/"} clientStore.Set(root_index, &root_client_info) user_id,user_secret := "zhanglei", "zhanglei_haha" user_index := user_id user_info := models.Client{ID: user_id, Secret: user_secret, Domain: "http://ip_of_web:80/"} clientStore.Set(user_index, &user_info) manager.MapClientStorage(clientStore) /* 搭建登录服务,用来给用户生成short live token(2h内有效) */ http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { //need:grant_typ(password),client_id,password if r.Method == "POST" { /* 当客户端POST的client存在且与password对应时,则认证服务器产生一个token. 否则,返回失败 */ err := srv.HandleTokenRequest(w, r) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } return } outputHTML(w, r, "static/login.html") }) http.HandleFunc("/vaild_token", func(w http.ResponseWriter, r *http.Request) { //need:grant_typ(password),client_id,password /* 提供HTTP的认证服务 */ log.Println("receive a vaild token request !\n") ctx := r.Context() r.ParseForm() access_token := r.Form.Get("token") log.Println("access token:", access_token) if access_token == "" { http.Error(w, "The request don't have any access token", http.StatusBadRequest) return } /* 查看该token是否被认证过,如果没有,则返回错误 */ ti, err := srv.Manager.LoadAccessToken(ctx, access_token) if err != nil { http.Error(w, "Found token error", http.StatusForbidden) return } response_map := make(map[string]interface{}) /* active = true表示认证成功 active、username将会被放入鉴权信息的头部 */ response_map["active"] = true response_map["username"] = ti.GetUserID() response_json, err := json.Marshal(response_map) _ = err response_string := string(response_json) io.WriteString(w, response_string) }) http.HandleFunc("/access_engery", func(w http.ResponseWriter, r *http.Request) { /* 从HTTP头部信息中得到用户信息 */ clientID, require_resource, require_action, err := get_client_info(r) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } /* 如果有相应权限,则需要返回给oathkeeper 200状态码 */ if clientID == "admin" && require_resource == "web_server" && require_action == "read" { io.WriteString(w, clientID+" is accessible to "+require_resource) } else { http.Error(w, clientID+" is not accessible to "+require_resource, http.StatusBadRequest) } return })
oathkeeper的配置如下:
[ { "id": "allow-anonymous-with-header-mutator", "version": "v0.36.0-beta.4", "upstream": { "url": "http://ip_of_webserver:80/" }, "match": { "url": "<https|http>://ip_of_oathkeeper:4455/", "methods": [ "GET" ] }, "authenticators": [ { "handler": "oauth2_introspection" } ], "authorizer": { "handler": "remote", "config": { "headers": { "clientID": "{{ print .Extra.username }}", "require_resource": "web_server", "action": "read" } } }, "mutators": [ { "handler": "noop" } ] }, { "id": "user_login", "version": "v0.36.0-beta.4", "upstream": { "url": "http://ip_of_go_oath2:9096" }, "match": { "url": "<https|http>://ip_of_oathkeeper:4455/login", "methods": [ "GET","POST" ] }, "authenticators": [ { "handler": "noop" } ], "authorizer": { "handler": "allow" }, "mutators": [ { "handler": "noop" } ] } ]serve: proxy: port: 4455 # run the proxy at port 4455 api: port: 4456 # run the api at port 4456 access_rules: repositories: - file:///home/zhanglei/go_project/src/github.com/ory/oathkeeper/demo/rules.json authenticators: noop: enabled: true unauthorized: enabled: true oauth2_introspection: enabled: true config: introspection_url: http://ip_of_go_oath2:9096/vaild_token mutators: noop: enabled: true authorizers: allow: enabled: true remote: enabled: true config: remote: http://ip_of_go_oath2:9096/access_engery
这个配置的意思是,通过配置访问authenticators.oauth2_introspection.config.introspection_url对用户进行认证,然后通过访问
authorizers.remote.config.remote来进行远程鉴权。