Skip to content

Commit

Permalink
Teleport Machine ID tbot (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
tuladhar authored Nov 21, 2023
1 parent 78494ae commit 664cf60
Show file tree
Hide file tree
Showing 25 changed files with 647 additions and 246 deletions.
1 change: 1 addition & 0 deletions .nancy-ignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
#pkg:golang/k8s.io/[email protected]
CVE-2020-8561 until=2024-01-08 # k8s.io/[email protected]
CVE-2023-47108 until=2024-01-08 # go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/[email protected]
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Adds support for Teleport Machine ID Bot for short-lived certificate for Teleport Cluster access.

### Fixed

- Fixes broken architecture diagram in README
Expand Down
15 changes: 15 additions & 0 deletions helm/teleport-operator/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "resource.default.name" . }}
namespace: {{ include "resource.default.namespace" . }}
labels:
{{- include "labels.common" . | nindent 4 }}
type: Opaque
data:
appCatalog: {{ .Values.teleport.appCatalog | quote }}
appName: {{ .Values.teleport.appName | quote }}
appVersion: {{ .Values.teleport.appVersion | quote }}
managementClusterName: {{ .Values.teleport.managementClusterName | quote }}
proxyAddr: {{ .Values.teleport.proxyAddr | quote }}
teleportVersion: {{ .Values.teleport.teleportVersion | quote }}
1 change: 1 addition & 0 deletions helm/teleport-operator/templates/psp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ spec:
volumes:
- 'projected'
- 'secret'
- 'configMap'
{{- end }}
16 changes: 0 additions & 16 deletions helm/teleport-operator/templates/secret.yaml

This file was deleted.

27 changes: 27 additions & 0 deletions helm/teleport-operator/templates/tbot-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "resource.default.name" . }}-tbot-config
namespace: {{ include "resource.default.namespace" . }}
labels:
{{- include "labels.common" . | nindent 4 }}
data:
tbot.yaml: |
version: v2
onboarding:
join_method: kubernetes
# ensure token is set to the name of the join token you created earlier
token: {{ .Values.teleport.managementClusterName }}-bot
storage:
# a memory destination is used for the bots own state since the kubernetes
# join method does not require persistence.
type: memory
# ensure this is configured to the address of your Teleport Proxy or
# Auth Server. Prefer the address of the Teleport Proxy.
auth_server: {{ .Values.teleport.proxyAddr }}
# outputs will be filled in during the completion of an access guide.
outputs:
- type: identity
destination:
type: kubernetes_secret
name: identity-output
74 changes: 74 additions & 0 deletions helm/teleport-operator/templates/tbot-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "resource.default.name" . }}-tbot
namespace: {{ include "resource.default.namespace" . }}
labels:
{{- include "labels.common" . | nindent 4 }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: tbot
template:
metadata:
labels:
app.kubernetes.io/name: tbot
spec:
securityContext:
runAsUser: {{ .Values.pod.user.id }}
runAsGroup: {{ .Values.pod.group.id }}
{{- with .Values.podSecurityContext }}
{{- . | toYaml | nindent 8 }}
{{- end }}
containers:
- name: tbot
image: public.ecr.aws/gravitational/teleport:{{ .Values.teleport.teleportVersion }}
command:
- tbot
args:
- start
- -c
- /config/tbot.yaml
env:
# POD_NAMESPACE is required for the kubernetes_secret` destination
# type to work correctly.
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
# KUBERNETES_TOKEN_PATH specifies the path to the service account
# JWT to use for joining.
# This path is based on the configuration of the volume and
# volumeMount.
- name: KUBERNETES_TOKEN_PATH
value: /var/run/secrets/tokens/join-sa-token
volumeMounts:
- mountPath: /config
name: config
- mountPath: /var/run/secrets/tokens
name: join-sa-token
securityContext:
{{- with .Values.containerSecurityContext }}
{{- . | toYaml | nindent 10 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 10 }}
serviceAccountName: {{ include "resource.default.name" . }}
volumes:
- name: config
configMap:
name: {{ include "resource.default.name" . }}-tbot-config
- name: join-sa-token
projected:
sources:
- serviceAccountToken:
path: join-sa-token
# 600 seconds is the minimum that Kubernetes supports. We
# recommend this value is used.
expirationSeconds: 600
# `example.teleport.sh` must be replaced with the name of
# your Teleport cluster.
audience: {{ .Values.teleport.teleportClusterName }}
3 changes: 3 additions & 0 deletions helm/teleport-operator/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"proxyAddr": {
"type": "string"
},
"teleportClusterName": {
"type": "string"
},
"teleportVersion": {
"type": "string"
}
Expand Down
4 changes: 2 additions & 2 deletions helm/teleport-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ teleport:
appVersion: 0.3.0
managementClusterName: ""
proxyAddr: test.teleport.giantswarm.io:443
teleportVersion: 13.1.5
identityFile: ""
teleportClusterName: test.teleport.giantswarm.io
teleportVersion: 14.1.1

pod:
user:
Expand Down
39 changes: 33 additions & 6 deletions internal/controller/cluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,23 @@ import (
capi "sigs.k8s.io/cluster-api/api/v1beta1"
ctrl "sigs.k8s.io/controller-runtime"

"github.com/giantswarm/teleport-operator/internal/pkg/config"
"github.com/giantswarm/teleport-operator/internal/pkg/key"
"github.com/giantswarm/teleport-operator/internal/pkg/teleport"

"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

const identityExpirationPeriod = 20 * time.Minute

// ClusterReconciler reconciles a Cluster object
type ClusterReconciler struct {
Client client.Client
Log logr.Logger
Scheme *runtime.Scheme
Teleport *teleport.Teleport
Client client.Client
Log logr.Logger
Scheme *runtime.Scheme
Teleport *teleport.Teleport
Namespace string
}

//+kubebuilder:rbac:groups=cluster.x-k8s.io.giantswarm.io,resources=clusters,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -68,9 +72,32 @@ func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
}
log.Info("Reconciling cluster", "cluster", cluster)

if r.Teleport.Identity != nil {
log.Info("Teleport identity", "last-read-minutes-ago", r.Teleport.Identity.Age(), "hash", r.Teleport.Identity.Hash())
}

if r.Teleport.Identity == nil || time.Since(r.Teleport.Identity.LastRead) > identityExpirationPeriod {
log.Info("Retrieving new identity", "secretName", key.TeleportBotSecretName)

newIdentityConfig, err := config.GetIdentityConfigFromSecret(ctx, r.Client, r.Namespace)
if err != nil {
return ctrl.Result{}, microerror.Mask(err)
}

if r.Teleport.TeleportClient, err = teleport.NewClient(ctx, r.Teleport.Config.ProxyAddr, newIdentityConfig.IdentityFile); err != nil {
return ctrl.Result{}, microerror.Mask(err)
}
if r.Teleport.Identity == nil {
log.Info("Connected to teleport cluster", "proxyAddr", r.Teleport.Config.ProxyAddr)
} else {
log.Info("Re-connected to teleport cluster with new identity", "proxyAddr", r.Teleport.Config.ProxyAddr)
}
r.Teleport.Identity = newIdentityConfig
}

registerName := cluster.Name
if cluster.Name != r.Teleport.SecretConfig.ManagementClusterName {
registerName = key.GetRegisterName(r.Teleport.SecretConfig.ManagementClusterName, cluster.Name)
if cluster.Name != r.Teleport.Config.ManagementClusterName {
registerName = key.GetRegisterName(r.Teleport.Config.ManagementClusterName, cluster.Name)
}

// Check if the cluster instance is marked to be deleted, which is indicated by the deletion timestamp being set.
Expand Down
Loading

0 comments on commit 664cf60

Please sign in to comment.