Skip to content

Commit

Permalink
client credentials auth flow + autoregistration (#46)
Browse files Browse the repository at this point in the history
This PR enables auto registration and client credentials auth flow.

Signed-off-by: Zahari Dichev <[email protected]>
  • Loading branch information
zaharidichev authored Mar 9, 2022
1 parent 0c76a57 commit 84ef4af
Show file tree
Hide file tree
Showing 57 changed files with 1,725 additions and 1,110 deletions.
60 changes: 53 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Usage:

Available Commands:
check Check the Buoyant Cloud agent installation for potential problems
completion Generate the autocompletion script for the specified shell
dashboard Open the Buoyant Cloud dashboard
help Help about any command
install Output Buoyant Cloud agent manifest for installation
uninstall Output Kubernetes manifest to uninstall the Buoyant Cloud agent
Expand All @@ -56,12 +58,53 @@ Use "linkerd-buoyant [command] --help" for more information about a command.

### Agent

Build and run:
#### Setup credentials and env vars

```
export BUOYANT_CLOUD_CLIENT_ID="org-client-id"
export BUOYANT_CLOUD_CLIENT_SECRET="org-client-secret"
export AGENT_NAME="agent-name"
```

#### Build and run agent registrator
```bash
bin/go-run agent
# Create agent-metadata config map with the desired agent name
cat <<EOF | kubectl apply -f -
kind: Namespace
apiVersion: v1
metadata:
name: buoyant-cloud
annotations:
linkerd.io/inject: enabled
labels:
app.kubernetes.io/part-of: buoyant-cloud
linkerd.io/extension: buoyant
---
kind: ConfigMap
metadata:
name: agent-metadata
namespace: buoyant-cloud
labels:
app.kubernetes.io/part-of: buoyant-cloud
apiVersion: v1
data:
agent_name: $AGENT_NAME
EOF

# Run the registrator
bin/go-run agent registrator
```

#### Build and run agent
```bash
# Read the agent id field (populated by the registrator) in the agent-metadata config map
export AGENT_ID=$(kubectl get cm/agent-metadata -n buoyant-cloud -o jsonpath='{.data.agent_id}')

# Run the agent with the agent id
bin/go-run agent agent --agent-id=$AGENT_ID
```

Docker build:
#### Docker build:
```bash
docker buildx build -f agent/Dockerfile -t ghcr.io/buoyantio/linkerd-buoyant:latest .
```
Expand Down Expand Up @@ -111,11 +154,14 @@ Install:
helm install --create-namespace --namespace buoyant-cloud --values charts/linkerd-buoyant/ci/fake-values.yaml linkerd-buoyant charts/linkerd-buoyant
```

To install a live agent from buoyant.cloud, register a new agent, get its
`values.yml`:
To install a live agent from buoyant.cloud you need to obtain its
`values.yml`. To obtain a values file, head over to
https://buoyant.cloud/settings?helm=1. In case you have only one pair
of org credentials, you will be prompted with a dialog that contains
the helm values. Otherwise, you can pick the exact credentials pair and
click on Helm usage. Save the values into `agent-values.yaml`.
```bash
VALUES_URL=https://buoyant.cloud/agent-helm-values/buoyant-cloud-k8s-XXX.yml
helm install --create-namespace --namespace buoyant-cloud --values $VALUES_URL linkerd-buoyant charts/linkerd-buoyant
helm install --create-namespace --namespace buoyant-cloud --values agent-values.yaml --set metadata.agentName=$AGENT_NAME linkerd-buoyant charts/linkerd-buoyant
```

Update chart README.md:
Expand Down
1 change: 1 addition & 0 deletions agent/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ RUN go mod download

COPY agent/main.go agent/main.go
COPY agent/pkg agent/pkg
COPY agent/cmd agent/cmd
COPY gen gen
RUN CGO_ENABLED=0 GOOS=linux go build -o /bcloud-agent -mod=readonly agent/main.go

Expand Down
145 changes: 145 additions & 0 deletions agent/cmd/agent/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package agent

import (
"context"
"crypto/tls"
"flag"
"os"
"os/signal"
"syscall"
"time"

"github.com/buoyantio/linkerd-buoyant/agent/pkg/api"
"github.com/buoyantio/linkerd-buoyant/agent/pkg/bcloudapi"
"github.com/buoyantio/linkerd-buoyant/agent/pkg/flags"
"github.com/buoyantio/linkerd-buoyant/agent/pkg/handler"
"github.com/buoyantio/linkerd-buoyant/agent/pkg/k8s"
pb "github.com/buoyantio/linkerd-buoyant/gen/bcloud"
l5dApi "github.com/linkerd/linkerd2/controller/gen/client/clientset/versioned"
"github.com/linkerd/linkerd2/pkg/admin"
l5dk8s "github.com/linkerd/linkerd2/pkg/k8s"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/informers"
"k8s.io/client-go/tools/clientcmd"
)

func dieIf(err error) {
if err != nil {
log.Fatal(err.Error())
}
}

// Main executes the agent subcommand
func Main(args []string) {
cmd := flag.NewFlagSet("agent", flag.ExitOnError)

apiAddr := cmd.String("api-addr", "api.buoyant.cloud:443", "address of the Buoyant Cloud API")
adminAddr := cmd.String("admin-addr", ":9990", "address of agent admin server")
grpcAddr := cmd.String("grpc-addr", "api.buoyant.cloud:443", "address of the Buoyant Cloud gRPC API")
kubeConfigPath := cmd.String("kubeconfig", "", "path to kube config")
localMode := cmd.Bool("local-mode", false, "enable port forwarding for local development")
noTLS := cmd.Bool("no-tls", false, "disable TLS in development mode")
agentID := cmd.String("agent-id", "", "the ID of the agent")

clientID, clientSecret := flags.ConfigureAndParseAgentParams(cmd, args)
if agentID == nil || *agentID == "" {
log.Fatal("missing agent id! set -agent-id flag")
}

// handle interrupts
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
shutdown := make(chan struct{}, 1)

// setup kubernetes clients and shared informers
rules := clientcmd.NewDefaultClientConfigLoadingRules()
if *kubeConfigPath != "" {
rules.ExplicitPath = *kubeConfigPath
}

k8sConfig, err := clientcmd.
NewNonInteractiveDeferredLoadingClientConfig(rules, &clientcmd.ConfigOverrides{}).
ClientConfig()
dieIf(err)

k8sAPI, err := l5dk8s.NewAPIForConfig(k8sConfig, "", nil, 0)
dieIf(err)

l5dClient, err := l5dApi.NewForConfig(k8sConfig)
dieIf(err)

sharedInformers := informers.NewSharedInformerFactory(k8sAPI.Interface, 10*time.Minute)

k8sClient := k8s.NewClient(sharedInformers, k8sAPI, l5dClient, *localMode)

// wait for discovery API to load

log.Info("waiting for Kubernetes API availability")
populateGroupList := func() (done bool, err error) {
_, err = k8sAPI.Discovery().ServerGroups()
if err != nil {
log.Debug("cannot reach Kubernetes API; retrying")
return false, nil
}
log.Info("Kubernetes API reached")
return true, nil
}
err = wait.PollImmediate(time.Second, time.Minute, populateGroupList)
dieIf(err)

// create bcloud grpc api client and streams

bcloudApiClient := bcloudapi.New(clientID, clientSecret, *apiAddr, *noTLS)
perRPCCreds := bcloudApiClient.Credentials(context.Background(), *agentID)

tlsCreds := credentials.NewTLS(&tls.Config{})
if *noTLS {
tlsCreds = insecure.NewCredentials()
}

conn, err := grpc.Dial(
*grpcAddr,
grpc.WithPerRPCCredentials(perRPCCreds),
grpc.WithTransportCredentials(tlsCreds),
)
dieIf(err)

bcloudClient := pb.NewApiClient(conn)
apiClient := api.NewClient(bcloudClient)

// create handlers
eventHandler := handler.NewEvent(k8sClient, apiClient)
workloadHandler := handler.NewWorkload(k8sClient, apiClient)

linkerdInfoHandler := handler.NewLinkerdInfo(k8sClient, apiClient)
manageAgentHandler := handler.NewManageAgent(k8sClient, apiClient)

// start shared informer and wait for sync
err = k8sClient.Sync(shutdown, 60*time.Second)
dieIf(err)

// start api client stream management logic
go apiClient.Start()

// start handlers
go eventHandler.Start(sharedInformers)
go workloadHandler.Start(sharedInformers)
go linkerdInfoHandler.Start()
go manageAgentHandler.Start()

// run admin server
adminServer := admin.NewServer(*adminAddr)
go adminServer.ListenAndServe()

// wait for shutdown
<-stop
log.Info("shutting down")
workloadHandler.Stop()
linkerdInfoHandler.Stop()
manageAgentHandler.Stop()
close(shutdown)
}
54 changes: 54 additions & 0 deletions agent/cmd/registrator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package registrator

import (
"context"
"flag"

"github.com/buoyantio/linkerd-buoyant/agent/pkg/bcloudapi"
"github.com/buoyantio/linkerd-buoyant/agent/pkg/flags"
"github.com/buoyantio/linkerd-buoyant/agent/pkg/registrator"
l5dk8s "github.com/linkerd/linkerd2/pkg/k8s"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/tools/clientcmd"
)

func dieIf(err error) {
if err != nil {
log.Fatal(err.Error())
}
}

// Main executes the registrator subcommand
func Main(args []string) {
cmd := flag.NewFlagSet("registrator", flag.ExitOnError)

apiAddr := cmd.String("api-addr", "api.buoyant.cloud:443", "address of the Buoyant Cloud API")
kubeConfigPath := cmd.String("kubeconfig", "", "path to kube config")
noTLS := cmd.Bool("no-tls", false, "disable TLS in development mode")
agentMetadataMap := cmd.String("agent-metadata-map", "agent-metadata", "the name of the agent metadata map")

clientID, clientSecret := flags.ConfigureAndParseAgentParams(cmd, args)

// setup kubernetes client
rules := clientcmd.NewDefaultClientConfigLoadingRules()
if *kubeConfigPath != "" {
rules.ExplicitPath = *kubeConfigPath
}

k8sConfig, err := clientcmd.
NewNonInteractiveDeferredLoadingClientConfig(rules, &clientcmd.ConfigOverrides{}).
ClientConfig()
dieIf(err)

k8sAPI, err := l5dk8s.NewAPIForConfig(k8sConfig, "", nil, 0)
dieIf(err)

// perform agent registration

bcloudApiClient := bcloudapi.New(clientID, clientSecret, *apiAddr, *noTLS)
agentRegistrator := registrator.New(bcloudApiClient, k8sAPI, *agentMetadataMap)

agentInfo, err := agentRegistrator.EnsureRegistered(context.Background())
dieIf(err)
log.Infof("Obtained agent info: %+v", agentInfo)
}
Loading

0 comments on commit 84ef4af

Please sign in to comment.