Enveloppez la lecture et l'écriture de GCP dans Secret Manager avec les sous-commandes google

thème

GCP dispose de Secret Manager. Ceux qui connaissent Kubernetes peuvent penser: "Est-ce quelque chose comme Secrets?" Cependant, comme les secrets k8s sont encodés en Base64, ils ne peuvent pas être téléchargés vers un référentiel GitHub public (par exemple, en tant que nom pour gérer les données d'origine), mais Secret Manager est crypté (si vous ne connaissez pas la clé) Exactement secret.

Voir ci-dessous pour d'autres choses comme Uri. https://cloud.google.com/blog/ja/products/identity-security/introducing-google-clouds-secret-manager

Comme mentionné dans l'article de référence ci-dessus, non seulement Secret Manager, mais également les services GCP peuvent être préparés à l'aide du SDK Cloud. Vous pouvez facilement exploiter des ressources sur GCP simplement en appuyant sur. Ainsi, comme dans cette rubrique, il n'est pas nécessaire d'écrire un wrapper par programmation. Cette fois, j'ai simplement encapsulé la fonction pour faire fonctionner Secret Manager comme sujet pour écrire un outil de ligne de commande approprié à l'aide de la bibliothèque subcommands de google.

Lecteur supposé

--Je connais GCP.

supposition

Environnement de développement

OS - Linux(Ubuntu)

$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"

#Backend

#Langue --Golang

$ go version
go version go1.15.2 linux/amd64

IDE - Goland

GoLand 2020.2.3
Build #GO-202.7319.61, built on September 16, 2020

Toutes les sources cette fois

https://github.com/sky0621/gcp-toolbox/tree/v0.1.0/secret-manager

Source individuelle

Je pensais écrire un commentaire, mais c'est presque toutes les promesses lors de l'utilisation des sous-commandes de Google et les promesses lors de l'utilisation du SDK Secret Manager (c'est-à-dire les informations sur chaque site). Pas d'explication.

main.go

En tant que fonction, seules la commande " create "pour créer un secret et la commande" list" pour afficher une liste des secrets créés sont préparées.

package main

import (
	"context"
	"flag"
	"os"

	"github.com/google/subcommands"
)

func main() {
	os.Exit(int(execMain()))
}

func execMain() subcommands.ExitStatus {
	subcommands.Register(subcommands.HelpCommand(), "")
	subcommands.Register(newCreateCmd(), "create")
	subcommands.Register(newListCmd(), "list")
	flag.Parse()
	return subcommands.Execute(context.Background())
}

create.go

package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"log"

	secretmanager "cloud.google.com/go/secretmanager/apiv1"
	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"

	"github.com/google/subcommands"
)

type createCmd struct {
	projectID, key, value, path string
}

func newCreateCmd() *createCmd {
	return &createCmd{}
}

func (*createCmd) Name() string {
	return "create"
}

func (*createCmd) Synopsis() string {
	return "create secret"
}

func (*createCmd) Usage() string {
	return `usage: create secret`
}

func (cmd *createCmd) SetFlags(f *flag.FlagSet) {
	f.StringVar(&cmd.projectID, "p", "", "project id")
	f.StringVar(&cmd.key, "k", "", "key")
	f.StringVar(&cmd.value, "v", "", "value")
	f.StringVar(&cmd.path, "f", "", "file path")
}

func (cmd *createCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if cmd.projectID == "" || cmd.key == "" || (cmd.value == "" && cmd.path == "") {
		log.Println("need -p [gcp project id] -k [secret key] -v [secret value] or -f [secret file path]")
		return subcommands.ExitFailure
	}

	var client *secretmanager.Client
	{
		var err error
		client, err = secretmanager.NewClient(ctx)
		if err != nil {
			log.Fatalf("failed to setup client: %v", err)
		}
	}

	// Create the request to create the secret.
	createSecretReq := &secretmanagerpb.CreateSecretRequest{
		Parent:   fmt.Sprintf("projects/%s", cmd.projectID),
		SecretId: cmd.key,
		Secret: &secretmanagerpb.Secret{
			Replication: &secretmanagerpb.Replication{
				Replication: &secretmanagerpb.Replication_Automatic_{
					Automatic: &secretmanagerpb.Replication_Automatic{},
				},
			},
		},
	}

	var secret *secretmanagerpb.Secret
	{
		var err error
		secret, err = client.CreateSecret(ctx, createSecretReq)
		if err != nil {
			log.Fatalf("failed to create secret: %v", err)
		}
	}

	// Declare the payload to storage.
	var payload []byte
	if cmd.value != "" {
		payload = []byte(cmd.value)
	}
	if cmd.path != "" {
		ba, err := ioutil.ReadFile(cmd.path)
		if err != nil {
			log.Fatalf("failed to read secret file: %+v", err)
		}
		payload = ba
	}
	if payload == nil {
		log.Fatal("payload is nil")
	}

	// Build the request.
	addSecretVersionReq := &secretmanagerpb.AddSecretVersionRequest{
		Parent: secret.Name,
		Payload: &secretmanagerpb.SecretPayload{
			Data: payload,
		},
	}

	var accessRequest *secretmanagerpb.AccessSecretVersionRequest
	{
		// Call the API.
		version, err := client.AddSecretVersion(ctx, addSecretVersionReq)
		if err != nil {
			log.Fatalf("failed to add secret version: %v", err)
		}

		// Build the request.
		accessRequest = &secretmanagerpb.AccessSecretVersionRequest{
			Name: version.Name,
		}
	}

	// Call the API.
	result, err := client.AccessSecretVersion(ctx, accessRequest)
	if err != nil {
		log.Fatalf("failed to access secret version: %v", err)
	}

	// Print the secret payload.
	//
	// WARNING: Do not print the secret in a production environment - this
	// snippet is showing how to access the secret material.
	log.Printf("Plaintext: %s", result.Payload.Data)

	return subcommands.ExitSuccess
}

list.go

package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"strings"

	secretmanager "cloud.google.com/go/secretmanager/apiv1"
	"google.golang.org/api/iterator"
	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"

	"github.com/google/subcommands"
)

type listCmd struct {
	projectID string
}

func newListCmd() *listCmd {
	return &listCmd{}
}

func (*listCmd) Name() string {
	return "list"
}

func (*listCmd) Synopsis() string {
	return "list secrets"
}

func (*listCmd) Usage() string {
	return `usage: list secrets`
}

func (cmd *listCmd) SetFlags(f *flag.FlagSet) {
	f.StringVar(&cmd.projectID, "p", "", "project id")
}

func (cmd *listCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if cmd.projectID == "" {
		log.Println("need -p [gcp project id]")
		return subcommands.ExitFailure
	}

	client, err := secretmanager.NewClient(ctx)
	if err != nil {
		log.Printf("failed to create secretmanager client: %v", err)
		return subcommands.ExitFailure
	}

	// Build the request.
	req := &secretmanagerpb.ListSecretsRequest{
		Parent: fmt.Sprintf("projects/%s", cmd.projectID),
	}

	// Call the API.
	it := client.ListSecrets(ctx, req)
	for {
		resp, err := it.Next()
		if err == iterator.Done {
			break
		}

		if err != nil {
			log.Printf("failed to list secret versions: %v", err)
			return subcommands.ExitFailure
		}

		names := strings.Split(resp.Name, "/")
		reqName := fmt.Sprintf("projects/%s/secrets/%s/versions/%s", names[1], names[3], "latest")

		// Build the request.
		req := &secretmanagerpb.AccessSecretVersionRequest{
			Name: reqName,
		}

		// Call the API.
		result, err := client.AccessSecretVersion(ctx, req)
		if err != nil {
			log.Printf("failed to access secret version: %v", err)
			return subcommands.ExitFailure
		}

		log.Printf("Found secret %s ... got value: %s\n", resp.Name, string(result.Payload.Data))
	}
	return subcommands.ExitSuccess
}

Entraine toi

Ajouter un secret

$ go run ./*.go create -p XXXXXXXX -k rdb-host -v localhost
2020/10/11 23:03:33 Plaintext: localhost
$ go run ./*.go create -p XXXXXXXX -k rdb-port -v 12345
2020/10/11 23:04:05 Plaintext: 12345
$ go run ./*.go create -p XXXXXXXX -k rdb-user -v user1
2020/10/11 23:04:24 Plaintext: user1
$ go run ./*.go create -p XXXXXXXX -k rdb-pass -v pass1234
2020/10/11 23:04:47 Plaintext: pass1234

En regardant le gestionnaire de console GCP, cela ressemble à ceci. screenshot-console.cloud.google.com-2020.10.11-23_05_07.png

Afficher la liste secrète

$ go run ./*.go list -p fs-work-21
2020/10/11 23:09:34 Found secret projects/999999999999/secrets/rdb-host ... got value: localhost
2020/10/11 23:09:35 Found secret projects/999999999999/secrets/rdb-pass ... got value: pass1234
2020/10/11 23:09:35 Found secret projects/999999999999/secrets/rdb-port ... got value: 12345
2020/10/11 23:09:35 Found secret projects/999999999999/secrets/rdb-user ... got value: user1

Sommaire

À quoi servent les informations secrètes gérées par Secret Manager, par exemple? Je pense qu'il y a différentes utilisations, mais dans mon cas, il est temps de transmettre un mot de passe DB ou quelque chose au service à publier sur Cloud Run via des variables d'environnement. Dans le shell de construction, définissez env le mot de passe DB obtenu à partir de Secret Manager avec la commande gcloud. Méthode spécifique Oui, je vous verrai plus tard.

Recommended Posts

Enveloppez la lecture et l'écriture de GCP dans Secret Manager avec les sous-commandes google
Exemple de lecture et d'écriture de CSV avec Python
Lire et écrire NetCDF avec Python
Lire et écrire du CSV avec Python
Lire et écrire des fichiers JSON avec Python
Script pour tweeter avec des multiples de 3 et des nombres avec 3 !!
La lecture et l'écriture s'adaptent aux fichiers avec Python (mémo)
Importez et supprimez des fichiers dans Google Cloud Storages avec django-storage
Lire et écrire des fichiers csv
React and Flask to GCP
Enregistrez le résultat de l'exploration avec Scrapy dans Google Data Store
Analyse de correspondance des phrases avec l'API COTOHA et sauvegarde dans un fichier
Essayez d'afficher la carte google et la carte géographique avec python
La meilleure façon d'utiliser MeCab et CaboCha avec Google Colab
Pour améliorer la réutilisabilité et la maintenabilité des flux de travail créés avec Luigi