ambassador网关之小试牛刀

Ambassador

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")
fmt.Println("RequestURI", r.RequestURI)
}

func lookHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, r.RequestURI)
fmt.Println("RequestURI", r.RequestURI)
}

func main() {
http.HandleFunc("/", lookHandler)
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 可以直接用我上传到阿里云的镜像仓库:

  • auth: registry.cn-shenzhen.aliyuncs.com/shikanon/ambassador-auth-demo:authv0.1
  • server: registry.cn-shenzhen.aliyuncs.com/shikanon/ambassador-auth-demo:serverv0.1

使用authservice服务

构建server

基于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/test/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/test/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进行重定向。

构建authservice

构建好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:80"
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,传给验证服务,验证通过了才转到应用服务。

需注意,在新版的Ambassador Edge Stack中,官方更推荐采用filter进行鉴权和过滤逻辑

apiVersion: v1
kind: Service
metadata:
name: app-auth
namespace: ambassador
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8000
selector:
name: app-auth
type: NodePort
---
apiVersion: getambassador.io/v2
kind: Filter
metadata:
name: app-auth
namespace: ambassador
spec:
External:
auth_service: "app-auth:80" # required
proto: "http" # optional; default is "http"
path_prefix: "/extauth"

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/test/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/test/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: /test/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

成功访问!

启用etcdctl排查etcd问题

由于集群是通过rancher启动的,etcd服务通过docker运行的,查看etcd启动配置:

$ docker inspect etcd | jq ".[].Config.Cmd"
[
"/usr/local/bin/etcd",
"--peer-cert-file=/etc/kubernetes/ssl/kube-etcd-10-17-1-44.pem",
"--initial-cluster-token=etcd-cluster-1",
"--name=etcd-localhost",
"--listen-peer-urls=https://0.0.0.0:2380",
"--initial-cluster=etcd-localhost=https://10.17.1.44:2380",
"--initial-cluster-state=new",
"--heartbeat-interval=500",
"--election-timeout=5000",
"--data-dir=/var/lib/rancher/etcd/",
"--advertise-client-urls=https://10.17.1.44:2379,https://10.17.1.44:4001",
"--listen-client-urls=https://0.0.0.0:2379",
"--peer-key-file=/etc/kubernetes/ssl/kube-etcd-10-17-1-44-key.pem",
"--enable-v2=true",
"--trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem",
"--cert-file=/etc/kubernetes/ssl/kube-etcd-10-17-1-44.pem",
"--key-file=/etc/kubernetes/ssl/kube-etcd-10-17-1-44-key.pem",
"--client-cert-auth=true",
"--initial-advertise-peer-urls=https://10.17.1.44:2380",
"--peer-trusted-ca-file=/etc/kubernetes/ssl/kube-ca.pem",
"--peer-client-cert-auth=true"
]

这里endpoints、cert、key、ca四个参数都可以找到,通过设置这四个参考,可以通过etcdctl访问etcd集群:

$ ./etcdctl --endpoints=https://10.17.1.44:2379  --cert=/etc/kubernetes/ssl/kube-etcd-10-17-1-44.pem --key=/etc/kubernetes/ssl/kube-etcd-10-17-1-44-key.pem --cacert=/etc/kubernetes/ssl/kube-ca.pem member list

5b0279579bcdfd25, started, etcd-localhost, https://10.17.1.44:2380, https://10.17.1.44:2379,https://10.17.1.44:4001, false

kubernetes的键值对都是存”/registry”的前缀,查询ambassador相关键值:

$ ./etcdctl --endpoints=https://10.17.1.44:2379  --cert=/etc/kubernetes/ssl/kube-etcd-10-17-1-44.pem --key=/etc/kubernetes/ssl/kube-etcd-10-17-1-44-key.pem --cacert=/etc/kubernetes/ssl/kube-ca.pem get "/registry" --prefix=true --keys-only | grep ambassador

/registry/apiextensions.k8s.io/customresourcedefinitions/authservices.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/consulresolvers.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/filterpolicies.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/filters.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/hosts.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/kubernetesendpointresolvers.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/kubernetesserviceresolvers.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/logservices.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/mappings.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/modules.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/ratelimits.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/ratelimitservices.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/tcpmappings.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/tlscontexts.getambassador.io
/registry/apiextensions.k8s.io/customresourcedefinitions/tracingservices.getambassador.io
/registry/apiregistration.k8s.io/apiservices/v1.getambassador.io
/registry/apiregistration.k8s.io/apiservices/v1beta1.getambassador.io
/registry/apiregistration.k8s.io/apiservices/v1beta2.getambassador.io
/registry/apiregistration.k8s.io/apiservices/v2.getambassador.io
/registry/clusterrolebindings/ambassador
/registry/clusterroles/ambassador
/registry/deployments/ambassador/ambassador
/registry/deployments/ambassador/ambassador-redis
/registry/events/ambassador/acmeclient.1611d42103881983
/registry/events/ambassador/acmeclient.1611d42a8f657b22
/registry/events/ambassador/acmeclient.1611d47c9a2d559e
/registry/events/ambassador/acmeclient.1611d487d1df6cb0
/registry/events/ambassador/acmeclient.1611d4d75611dcd7
/registry/events/ambassador/acmeclient.1611d4e362feb1b6
/registry/events/ambassador/acmeclient.1611d53357ac8137
/registry/events/ambassador/acmeclient.1611d53bd22dd4a2
/registry/events/ambassador/acmeclient.1611d58c6fda74a4
/registry/events/ambassador/acmeclient.1611d59770e65ef6
/registry/events/ambassador/acmeclient.1611d5ed8b9bbdaa
/registry/events/ambassador/acmeclient.1611d5f7bde30c2e
/registry/events/ambassador/acmeclient.1611d648ce16e271
/registry/events/ambassador/acmeclient.1611d6540f10c97c
/registry/events/ambassador/acmeclient.1611d6a71f391f4f
/registry/events/ambassador/acmeclient.1611d6b3387b89df
/registry/events/ambassador/ambassador-557999754d-c9x75.1611a47290ea5cce
/registry/events/ambassador/ambassador-557999754d-c9x75.1611a473b2092b90
/registry/events/ambassador/ambassador-557999754d-c9x75.1611a474c7ee3eef
/registry/events/ambassador/ambassador-557999754d-c9x75.1611a48b3d997657
/registry/events/ambassador/kale.1611d421033db815
/registry/events/ambassador/kale.1611d42a8f59d126
/registry/events/ambassador/kale.1611d47c99f771ec
/registry/events/ambassador/kale.1611d487d1c3addd
/registry/events/ambassador/kale.1611d4d755f315c8
/registry/events/ambassador/kale.1611d4e362d7ff0c
/registry/events/ambassador/kale.1611d5335793f860
/registry/events/ambassador/kale.1611d53bd1c28916
/registry/events/ambassador/kale.1611d58c6f8ebc8d
/registry/events/ambassador/kale.1611d59770e39acb
/registry/events/ambassador/kale.1611d5ed8ae22866
/registry/events/ambassador/kale.1611d5f7bd50574f
/registry/events/ambassador/kale.1611d648ce038245
/registry/events/ambassador/kale.1611d6540f3fefdf
/registry/events/ambassador/kale.1611d6a71ea592ca
/registry/events/ambassador/kale.1611d6b33778d9bd
/registry/getambassador.io/authservices/ambassador/ambassador-edge-stack-auth
/registry/getambassador.io/mappings/ambassador/ambassador-devportal
/registry/getambassador.io/mappings/ambassador/ambassador-devportal-api
/registry/getambassador.io/ratelimitservices/ambassador/ambassador-edge-stack-ratelimit
/registry/leases/ambassador/acmeclient

shikanon wechat
欢迎您扫一扫,订阅我滴↑↑↑的微信公众号!