这是一个坑…
Ambassador是一个云原生的API网关,主要用于为集群提供南-北网关,对外网流入的流量进行管理,包括限流、鉴权等。
今天这里用ambassador构建一个的鉴权服务。
k8s安装:
kubectl apply -f https://www.getambassador.io/yaml/aes-crds.yaml && \
kubectl wait --for condition=established --timeout=180s crd -lproduct=aes && \
kubectl apply -f https://www.getambassador.io/yaml/aes.yaml && \
kubectl -n ambassador wait --for condition=available --timeout=180s deploy -lproduct=aes
其他安装方式参考安装手册。
安装好后,查看services:
$ kubectl get svc -n ambassador
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ambassador LoadBalancer 10.43.227.83 <pending> 80:31082/TCP,443:32114/TCP 4h4m
ambassador-admin ClusterIP 10.43.143.56 <none> 8877/TCP 4h4m
ambassador-redis ClusterIP 10.43.59.72 <none> 6379/TCP 4h4m
由于这里没有LB,再启一个node-port:
apiVersion: v1
kind: Service
metadata:
labels:
app: ambassador-nodeport
name: ambassador-nodeport
spec:
externalTrafficPolicy: Cluster
ports:
- name: "80"
port: 80
protocol: TCP
targetPort: http
- name: "443"
port: 443
protocol: TCP
targetPort: https
selector:
service: ambassador
sessionAffinity: None
type: NodePort
直接访问机器的即可看到页面:
添加一个后端应用quote:
---
apiVersion: v1
kind: Service
metadata:
name: quote
namespace: ambassador
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: quote
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: quote
namespace: ambassador
spec:
replicas: 1
selector:
matchLabels:
app: quote
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: quote
spec:
containers:
- name: backend
image: registry.cn-shenzhen.aliyuncs.com/shikanon/ambassador-auth-demo:serverv0.1
ports:
- name: http
containerPort: 8080
---
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
name: quote-backend
namespace: ambassador
spec:
prefix: /backend/
service: quote
注:这里Deployment
的port用name名称叫”http”,所以service可以写成targetPort: http
测试
注意:由于这里用的是Ambassador Edge Stack
版本,访问必须用https的端口:
$ kubectl get svc -nambassador NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ambassador LoadBalancer 10.43.86.4 80:32487/TCP,443:31632/TCP 4h9m ambassador-admin ClusterIP 10.43.79.19 8877/TCP 4h9m ambassador-nodeport NodePort 10.43.228.37 80:30930/TCP,443:32089/TCP 7m36s ambassador-redis ClusterIP 10.43.191.197 6379/TCP 4h9m app-auth NodePort 10.43.206.119 80:30176/TCP 27m app-server ClusterIP 10.43.174.101 80/TCP 27m quote ClusterIP 10.43.55.41 80/TCP 7m2s
必须用https访问
$ curl https://10.43.86.4/backend/ –insecure
基于golang
构建一个简单的鉴权服务:
package main
import (
"fmt"
"net/http"
)
func indexHandler(w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username")
passwd := r.URL.Query().Get("passwd")
content := fmt.Sprintf("username:%v", username)
if username == "shikanon" && passwd == "123456" {
w.WriteHeader(200)
} else {
w.WriteHeader(403)
}
fmt.Fprintf(w, content)
}
func main() {
http.HandleFunc("/", indexHandler)
http.ListenAndServe(":8000", nil)
}
构建一个简单的server服务:
package main
import (
"fmt"
"net/http"
)
func indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "this is server")
}
func main() {
http.HandleFunc("/lookup", indexHandler)
http.ListenAndServe(":8080", nil)
}
编写Dockerfile(两个服务的dockerfile类似):
FROM golang:1.14.3-alpine as build
COPY . /app
WORKDIR /app
RUN go build -o auth main.go
RUN chmod +x ./auth
FROM alpine:latest
LABEL maintainer="hexo-shikanon-blog <shikanon@tensorbytes.com>"
COPY --from=build /app /app
EXPOSE 8000
CMD ["/app/auth"]
不想自己 build 可以直接用我上传到阿里云的镜像仓库:
基于server镜像构建响应服务:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-server
namespace: ambassador
labels:
name: app-server
spec:
replicas: 1
selector:
matchLabels:
name: app-server
template:
metadata:
labels:
name: app-server
spec:
containers:
- name: app-server
image: registry.cn-shenzhen.aliyuncs.com/shikanon/ambassador-auth-demo:serverv0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: app-server
namespace: ambassador
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
name: app-server
构建mapping实现ambassador转发:
apiVersion: getambassador.io/v2
kind: Mapping
metadata:
name: auth-backend-test
namespace: ambassador
spec:
prefix: /test/
host_redirect: true
service: app-server
测试一下:
$ kubectl get svc -nambassador NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ambassador LoadBalancer 10.43.47.77 80:31290/TCP,443:31952/TCP 12h ambassador-admin ClusterIP 10.43.181.248 8877/TCP 12h ambassador-redis ClusterIP 10.43.65.217 6379/TCP 12h app-server ClusterIP 10.43.56.227 80/TCP 26m
测试效果:
$ curl -L http://10.43.47.77/test2/helloworld --insecure
* About to connect() to 10.43.47.77 port 80 (#0)
* Trying 10.43.47.77...
* Connected to 10.43.47.77 (10.43.47.77) port 80 (#0)
> GET /test2/helloworld HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.43.47.77
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< location: https://10.43.47.77/test2/helloworld
< date: Fri, 22 May 2020 02:03:46 GMT
< server: envoy
< content-length: 0
<
* Connection #0 to host 10.43.47.77 left intact
* Issue another request to this URL: 'https://10.43.47.77/test2/helloworld'
* Found bundle for host 10.43.47.77: 0x7fced0
* About to connect() to 10.43.47.77 port 443 (#1)
* Trying 10.43.47.77...
* Connected to 10.43.47.77 (10.43.47.77) port 443 (#1)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: O=Ambassador Edge Stack Self-Signed
* start date: May 21 13:32:32 2020 GMT
* expire date: May 21 13:32:32 2021 GMT
* common name: (nil)
* issuer: O=Ambassador Edge Stack Self-Signed
> GET /test2/helloworld HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.43.47.77
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Fri, 22 May 2020 02:03:46 GMT
< content-length: 11
< content-type: text/plain; charset=utf-8
< x-envoy-upstream-service-time: 0
< server: envoy
<
* Connection #1 to host 10.43.47.77 left intact
/helloworld
如果用http
协议,这里必须加-L
,因为ambassador会强制转成https
协议,并返回301进行重定向。
构建好server,下面就可以构建authservice做鉴权验证和转发:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-auth
namespace: ambassador
labels:
name: app-auth
spec:
replicas: 1
selector:
matchLabels:
name: app-auth
template:
metadata:
labels:
name: app-auth
spec:
containers:
- name: app-auth
image: registry.cn-shenzhen.aliyuncs.com/shikanon/ambassador-auth-demo:authv0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: app-auth
namespace: ambassador
annotations:
getambassador.io/config: |
---
apiVersion: getambassador.io/v2
kind: AuthService
name: authentication
auth_service: "app-auth:3000"
path_prefix: "/extauth"
allowed_request_headers:
- "x-qotm-session"
allowed_authorization_headers:
- "x-qotm-session"
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8000
selector:
name: app-auth
type: NodePort
这里设置了对extauth
的前缀做验证转发,也就是会匹配包含/extauth
的url,传给验证服务,验证通过了才转到应用服务。
okay,下面开始测试。
先查看service地址:
$ kubectl get svc -nambassador NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ambassador LoadBalancer 10.43.47.77 80:31290/TCP,443:31952/TCP 12h ambassador-admin ClusterIP 10.43.181.248 8877/TCP 12h ambassador-redis ClusterIP 10.43.65.217 6379/TCP 12h app-auth NodePort 10.43.179.149 80:32711/TCP 12h app-server ClusterIP 10.43.56.227 80/TCP 26m
测试:
$ curl -Lv https://10.43.47.77/extauth/test2/helloworld --insecure
* About to connect() to 10.43.47.77 port 443 (#0)
* Trying 10.43.47.77...
* Connected to 10.43.47.77 (10.43.47.77) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* skipping SSL peer certificate verification
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: O=Ambassador Edge Stack Self-Signed
* start date: May 21 13:32:32 2020 GMT
* expire date: May 21 13:32:32 2021 GMT
* common name: (nil)
* issuer: O=Ambassador Edge Stack Self-Signed
> GET /extauth/test2/helloworld HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.43.47.77
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< content-type: text/html; charset=utf-8
< location: /test2/helloworld
< date: Fri, 22 May 2020 02:31:35 GMT
< content-length: 52
< x-envoy-upstream-service-time: 0
< server: envoy
<
* Ignoring the response-body
* Connection #0 to host 10.43.47.77 left intact
* Issue another request to this URL: 'https://10.43.47.77/test2/helloworld'
* Found bundle for host 10.43.47.77: 0x1c73f20
* Re-using existing connection! (#0) with host 10.43.47.77
* Connected to 10.43.47.77 (10.43.47.77) port 443 (#0)
> GET /test2/helloworld HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 10.43.47.77
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Fri, 22 May 2020 02:31:35 GMT
< content-length: 11
< content-type: text/plain; charset=utf-8
< x-envoy-upstream-service-time: 0
< server: envoy
<
* Connection #0 to host 10.43.47.77 left intact
/helloworld
成功访问!