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.480:32487/TCP,443:31632/TCP 4h9m
ambassador-admin ClusterIP 10.43.79.198877/TCP 4h9m
ambassador-nodeport NodePort 10.43.228.3780:30930/TCP,443:32089/TCP 7m36s
ambassador-redis ClusterIP 10.43.191.1976379/TCP 4h9m
app-auth NodePort 10.43.206.11980:30176/TCP 27m
app-server ClusterIP 10.43.174.10180/TCP 27m
quote ClusterIP 10.43.55.4180/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.7780:31290/TCP,443:31952/TCP 12h
ambassador-admin ClusterIP 10.43.181.2488877/TCP 12h
ambassador-redis ClusterIP 10.43.65.2176379/TCP 12h
app-server ClusterIP 10.43.56.22780/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 |
这里设置了对extauth
的前缀做验证转发,也就是会匹配包含/extauth
的url,传给验证服务,验证通过了才转到应用服务。
需注意,在新版的Ambassador Edge Stack中,官方更推荐采用filter进行鉴权和过滤逻辑
apiVersion: v1 |
okay,下面开始测试。
先查看service地址:
$ kubectl get svc -nambassador
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ambassador LoadBalancer 10.43.47.7780:31290/TCP,443:31952/TCP 12h
ambassador-admin ClusterIP 10.43.181.2488877/TCP 12h
ambassador-redis ClusterIP 10.43.65.2176379/TCP 12h
app-auth NodePort 10.43.179.14980:32711/TCP 12h
app-server ClusterIP 10.43.56.22780/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