diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a98d6b5..d1c13d6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,3 +18,15 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} - run: make lint - run: make test-e2e + test-teleport: + name: Test with Teleport + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - uses: ./.github/actions/aqua + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + - run: make test-teleport diff --git a/Makefile b/Makefile index ad4c16b..220a965 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ PROTECTOR_IMG ?= login-protector:dev TRACKER_IMG ?= local-session-tracker:dev +TELEPORT_VERSION ?= 15.3.7 + # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) GOBIN=$(shell go env GOPATH)/bin @@ -59,6 +61,9 @@ vet: ## Run go vet against code. test-e2e: start-kind load-image deploy go test ./test/e2e/ -v -ginkgo.v +.PHONY: test-teleport # Run the e2e tests with Teleport. +test-teleport: start-kind-teleport load-image deploy-teleport + .PHONY: lint lint: setup ## Run golangci-lint linter & yamllint golangci-lint run @@ -112,6 +117,10 @@ endif start-kind: setup kind create cluster +.PHONY: start-kind-teleport +start-kind-teleport: setup + kind create cluster --config=./test/teleport/kind-config.yaml + .PHONY: stop-kind stop-kind: setup kind delete cluster @@ -142,22 +151,22 @@ setup: ##@ Teleport -.PHONY: get-teleport-manifests -get-teleport-manifests: +.PHONY: update-teleport-manifests +update-teleport-manifests: helm repo add teleport https://charts.releases.teleport.dev helm repo update helm template teleport --namespace teleport teleport/teleport-cluster \ --create-namespace \ - --version 15.3.7 \ + --version $(TELEPORT_VERSION) \ --values ./test/teleport/cluster/values.yaml \ > ./test/teleport/cluster/teleport-cluster.yaml .PHONY: setup-teleport-cli setup-teleport-cli: rm -rf teleport - wget https://cdn.teleport.dev/teleport-v15.3.7-linux-amd64-bin.tar.gz - tar -xvf teleport-v15.3.7-linux-amd64-bin.tar.gz - rm teleport-v15.3.7-linux-amd64-bin.tar.gz + wget https://cdn.teleport.dev/teleport-v$(TELEPORT_VERSION)-linux-amd64-bin.tar.gz + tar -xvf teleport-v$(TELEPORT_VERSION)-linux-amd64-bin.tar.gz + rm teleport-v$(TELEPORT_VERSION)-linux-amd64-bin.tar.gz .PHONY: deploy-teleport deploy-teleport: @@ -171,11 +180,23 @@ deploy-teleport: sed -i "s/auth_token: .*/auth_token: $$TOKEN/g" ./test/teleport/node/teleport-secret.yaml kustomize build ./test/teleport/node | kubectl apply -f - -.PHONY: create-teleport-users -create-teleport-users: + # Deploy tbot + kubectl create namespace login-protector-system + kubectl apply -f ./test/teleport/tbot/rbac.yaml kubectl exec -i -n teleport deployment/teleport-auth -- tctl create -f < ./test/teleport/role/member.yaml - kubectl exec -i -n teleport deployment/teleport-auth -- tctl users add myuser2 --roles=member,editor - kubectl exec -i -n teleport deployment/teleport-auth -- tctl create -f < ./test/teleport/role/api-access.yaml - kubectl exec -i -n teleport deployment/teleport-auth -- tctl users add api-access2 --roles=api-access + kubectl exec -i -n teleport deployment/teleport-auth -- tctl create -f < ./test/teleport/tbot/bot.yaml + kubectl proxy -p 8080 & + JWKS=$$(curl http://localhost:8080/openid/v1/jwks) && \ + sed -i "s/ jwks: .*/ jwks: \'$$JWKS\'/g" ./test/teleport/tbot/token.yaml + kubectl exec -i -n teleport deployment/teleport-auth -- tctl create -f < ./test/teleport/tbot/token.yaml + kubectl apply -f ./test/teleport/tbot/configmap.yaml + kubectl apply -f ./test/teleport/tbot/deployment.yaml + + # Deploy Login Protector with teleport-session-watcher + kustomize build ./config/teleport | kubectl apply -f - + kubectl -n login-protector-system wait --for=condition=available --timeout=180s --all deployments +login: + kubectl get secret -n login-protector-system identity-output -o json | jq -r .data.identity | base64 -d > identity + ./teleport/tsh -i ./identity --proxy --insecure localhost:3080 ssh cybozu@node-demo-0 diff --git a/cluster.yaml b/cluster.yaml index f98e1c6..c76a334 100644 --- a/cluster.yaml +++ b/cluster.yaml @@ -9,18 +9,3 @@ name: kind-login-protector-dev product: kind kubernetesVersion: v1.30.0 registry: ctlptl-registry -kindV1Alpha4Cluster: - nodes: - - role: control-plane - - role: worker - extraPortMappings: - - containerPort: 30080 - hostPort: 3080 - - containerPort: 30023 - hostPort: 3023 - - containerPort: 30024 - hostPort: 3024 - - containerPort: 30026 - hostPort: 3026 - - containerPort: 31025 - hostPort: 3025 diff --git a/cmd/login-protector/main.go b/cmd/login-protector/main.go index f8c6fb4..6af9b6d 100644 --- a/cmd/login-protector/main.go +++ b/cmd/login-protector/main.go @@ -3,10 +3,8 @@ package main import ( "crypto/tls" "flag" - teleport_client "github.com/gravitational/teleport/api/client" "log" "os" - "sigs.k8s.io/controller-runtime/pkg/manager" "strings" "time" @@ -15,6 +13,7 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth" "github.com/cybozu-go/login-protector/internal/controller" + teleport_client "github.com/gravitational/teleport/api/client" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -24,6 +23,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" //+kubebuilder:scaffold:imports @@ -48,7 +48,7 @@ func main() { var enableHTTP2 bool var sessionCheckInterval time.Duration var sessionWatcher string - var teleportApiToken string + var teleportIdentityFile string var teleportNamespace string var teleportAddrs string flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") @@ -62,9 +62,9 @@ func main() { "If set, HTTP/2 will be enabled for the metrics and webhook servers") flag.DurationVar(&sessionCheckInterval, "session-check-interval", 5*time.Second, "interval to check session") flag.StringVar(&sessionWatcher, "session-watcher", "local", "session watcher to use (local or teleport)") - flag.StringVar(&teleportApiToken, "teleport-api-token", "", "The file path of Teleport API token") + flag.StringVar(&teleportIdentityFile, "teleport-identity-file", "", "The file path of Teleport Identity") flag.StringVar(&teleportNamespace, "teleport-namespace", "teleport", "The namespace of Teleport") - flag.StringVar(&teleportAddrs, "teleport-addrs", "teleport:3080,teleport-auth:3025,teleport:3024", "The comma-separated list of Teleport addresses") + flag.StringVar(&teleportAddrs, "teleport-addrs", "teleport-auth:3025", "The comma-separated list of Teleport addresses") opts := zap.Options{ Development: true, } @@ -134,8 +134,8 @@ func main() { ch, ) case "teleport": - if teleportApiToken == "" { - setupLog.Error(nil, "teleport-api-token is required for teleport session watcher") + if teleportIdentityFile == "" { + setupLog.Error(nil, "teleport-identity-file is required for teleport session watcher") os.Exit(1) } if teleportNamespace == "" { @@ -149,14 +149,14 @@ func main() { teleportClient, err := teleport_client.New(ctx, teleport_client.Config{ Addrs: strings.Split(teleportAddrs, ","), Credentials: []teleport_client.Credentials{ - teleport_client.LoadIdentityFile(teleportApiToken), + teleport_client.LoadIdentityFile(teleportIdentityFile), }, }) if err != nil { log.Fatalf("failed to create client: %v", err) } - defer teleportClient.Close() + defer teleportClient.Close() // nolint: errcheck watcher = controller.NewTeleportSessionWatcher( mgr.GetClient(), teleportClient, diff --git a/config/teleport/kustomization.yaml b/config/teleport/kustomization.yaml index e4daf7d..5c93302 100644 --- a/config/teleport/kustomization.yaml +++ b/config/teleport/kustomization.yaml @@ -2,10 +2,3 @@ resources: - ../default patchesStrategicMerge: - ./manager.yaml -secretGenerator: - - name: teleport-api-token - namespace: login-protector-system - files: - - api-access.pem -generatorOptions: - disableNameSuffixHash: true diff --git a/config/teleport/manager.yaml b/config/teleport/manager.yaml index d4f1b70..5e49ad8 100644 --- a/config/teleport/manager.yaml +++ b/config/teleport/manager.yaml @@ -12,14 +12,14 @@ spec: args: - --leader-elect - --session-watcher=teleport - - --teleport-api-token=/etc/login-protector/api-access.pem + - --teleport-identity-file=/etc/login-protector/identity - --teleport-namespace=teleport - --teleport-addrs=teleport-auth.teleport.svc.cluster.local:3025 name: manager volumeMounts: - mountPath: /etc/login-protector - name: api-token + name: identity volumes: - - name: api-token + - name: identity secret: - secretName: teleport-api-token + secretName: identity-output diff --git a/test/teleport/README.md b/teleport.md similarity index 98% rename from test/teleport/README.md rename to teleport.md index 31d6ff0..bd82bed 100644 --- a/test/teleport/README.md +++ b/teleport.md @@ -56,6 +56,15 @@ make load-image $ kustomize build ./config/teleport | kubectl apply -f - ``` + +## Create Bot + + + +```console + +``` + ## Teleport Version Teleport rejects connections from clients running incompatible versions. diff --git a/test/teleport/cluster/teleport-cluster.yaml b/test/teleport/cluster/teleport-cluster.yaml index 6399cf5..5d3e7ef 100644 --- a/test/teleport/cluster/teleport-cluster.yaml +++ b/test/teleport/cluster/teleport-cluster.yaml @@ -116,6 +116,7 @@ data: mysql_listen_addr: 0.0.0.0:3036 public_addr: - localhost:3080 + - teleport.teleport.svc.cluster.local:443 tunnel_listen_addr: 0.0.0.0:3024 ssh_service: enabled: false @@ -488,7 +489,7 @@ spec: metadata: annotations: # ConfigMap checksum, to recreate the pod on config changes. - checksum/config: 679ed789012e348f3db624985790218307d4924c71aa0fb7091724de18afea4f + checksum/config: 59a655bea21e5dc97e318b7a4469b63f854d3bb82061c2cc2d60ba229fb3a698 labels: app.kubernetes.io/name: 'teleport-cluster' app.kubernetes.io/instance: 'teleport' diff --git a/test/teleport/cluster/values.yaml b/test/teleport/cluster/values.yaml index 78d300a..aa2886c 100644 --- a/test/teleport/cluster/values.yaml +++ b/test/teleport/cluster/values.yaml @@ -4,3 +4,4 @@ persistence: enabled: false publicAddr: - localhost:3080 + - teleport.teleport.svc.cluster.local:443 diff --git a/test/teleport/kind-config.yaml b/test/teleport/kind-config.yaml new file mode 100644 index 0000000..79200ed --- /dev/null +++ b/test/teleport/kind-config.yaml @@ -0,0 +1,16 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + - role: worker + extraportmappings: + - containerport: 30080 + hostport: 3080 + - containerport: 30023 + hostport: 3023 + - containerport: 30024 + hostport: 3024 + - containerport: 30026 + hostport: 3026 + - containerport: 31025 + hostport: 3025 diff --git a/test/teleport/node/teleport-secret.yaml b/test/teleport/node/teleport-secret.yaml index 5235f1d..4ff9363 100644 --- a/test/teleport/node/teleport-secret.yaml +++ b/test/teleport/node/teleport-secret.yaml @@ -10,7 +10,7 @@ stringData: data_dir: /var/lib/teleport auth_servers: - teleport-auth.teleport.svc.cluster.local:3025 - auth_token: 1719b33c8e674cb9096a102f8f688b26 + auth_token: 440bc74f6f5d57214ab8b328fada8c88 log: output: stderr severity: DEBUG diff --git a/test/teleport/role/api-access.yaml b/test/teleport/role/api-access.yaml index a237565..2426483 100644 --- a/test/teleport/role/api-access.yaml +++ b/test/teleport/role/api-access.yaml @@ -10,6 +10,4 @@ spec: - list - read deny: {} - options: - max_session_ttl: 87600h0m0s version: v7 diff --git a/test/teleport/tbot/bot.yaml b/test/teleport/tbot/bot.yaml new file mode 100644 index 0000000..5bdfda2 --- /dev/null +++ b/test/teleport/tbot/bot.yaml @@ -0,0 +1,8 @@ +kind: bot +version: v1 +metadata: + name: login-protector-bot +spec: + roles: + - api-access + - member diff --git a/test/teleport/tbot/configmap.yaml b/test/teleport/tbot/configmap.yaml new file mode 100644 index 0000000..2f9743d --- /dev/null +++ b/test/teleport/tbot/configmap.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: tbot-config + namespace: login-protector-system +data: + tbot.yaml: | + version: v2 + debug: true + onboarding: + join_method: kubernetes + token: login-protector-bot-token + storage: + type: memory + auth_server: teleport-auth.teleport.svc.cluster.local:3025 + outputs: + - type: identity + destination: + type: kubernetes_secret + name: identity-output diff --git a/test/teleport/tbot/deployment.yaml b/test/teleport/tbot/deployment.yaml new file mode 100644 index 0000000..24a2633 --- /dev/null +++ b/test/teleport/tbot/deployment.yaml @@ -0,0 +1,52 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tbot + namespace: login-protector-system +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: tbot + template: + metadata: + labels: + app.kubernetes.io/name: tbot + spec: + containers: + - name: tbot + image: public.ecr.aws/gravitational/tbot-distroless:15.3.7 + args: + - start + - -c + - /config/tbot.yaml + - --insecure + - --debug + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: KUBERNETES_TOKEN_PATH + value: /var/run/secrets/tokens/join-sa-token + - name: TELEPORT_ANONYMOUS_TELEMETRY + value: "1" + volumeMounts: + - mountPath: /config + name: config + - mountPath: /var/run/secrets/tokens + name: join-sa-token + serviceAccountName: tbot + volumes: + - name: config + configMap: + name: tbot-config + - name: join-sa-token + projected: + sources: + - serviceAccountToken: + path: join-sa-token + expirationSeconds: 600 + audience: teleport-demo diff --git a/test/teleport/tbot/rbac.yaml b/test/teleport/tbot/rbac.yaml new file mode 100644 index 0000000..f4e9ab9 --- /dev/null +++ b/test/teleport/tbot/rbac.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tbot + namespace: login-protector-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secrets-admin + namespace: login-protector-system +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["*"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: tbot-secrets-admin + namespace: login-protector-system +subjects: + - kind: ServiceAccount + name: tbot +roleRef: + kind: Role + name: secrets-admin + apiGroup: rbac.authorization.k8s.io diff --git a/test/teleport/tbot/token.yaml b/test/teleport/tbot/token.yaml new file mode 100644 index 0000000..5a5d571 --- /dev/null +++ b/test/teleport/tbot/token.yaml @@ -0,0 +1,14 @@ +kind: token +version: v2 +metadata: + name: login-protector-bot-token +spec: + roles: [Bot] + bot_name: login-protector-bot + join_method: kubernetes + kubernetes: + type: static_jwks + static_jwks: + jwks: '{"keys":[{"use":"sig","kty":"RSA","kid":"qLsyzPmCKUO44lIZzgQ3JCpGIQj9Sy3LN5yX0vLHPbk","alg":"RS256","n":"5-AEpTC7ehCiSSOaQuckHWRXasIinMgUPG2U0RgHKjqt_UqWk_wclJ58FiSO-nUhoUXFQpavKI62MHeA3lzhtR1q2Oea8CJoEMCFnThyUpnP3WfQk6KZdcM4Uv6l2Z3iNtDK73GP0es2f_iNHBIpS-0MWmTJrp4dGFnJPhZdLk50zrABoAg7WvE50Gw_2sLGtbTAUO4toLHYiqd7mPGsyFnPRC2xiP2bHR2uQ4xn25e5fMSWx7lCO-SOAY7UPE6J78VnTG-1_QLBlf4dk02akzaamIme3ystPlycsPlZ7YN3DT5u-RgQ_OUs9Z0fYrSBjuR_cU_YKaPri6nSC_0OSQ","e":"AQAB"}]}' + allow: + - service_account: "login-protector-system:tbot" # service_account