Skip to content

Commit

Permalink
Merge pull request #7 from sharathrnair87/feature/use_vault_go_sdk
Browse files Browse the repository at this point in the history
Features:
  • Loading branch information
DFW1N committed May 30, 2024
2 parents 16eb280 + 036fda7 commit 8978da9
Show file tree
Hide file tree
Showing 16 changed files with 1,014 additions and 922 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

# Overview

Vaultify is a powerful CLI tool developed in Go, designed to enhance productivity and security by encrypting the state-files in `base64` for managing Terraform state files. It streamlines the encryption and storage of state files across multiple platforms, including `HashiCorp Vault`, `Azure Storage Account`, and soon, `AWS S3 buckets`. By automating the encryption and push/pull processes, Vaultify ensures your Terraform state files are securely managed and easily accessible.
Vaultify is a powerful CLI tool developed in Go, designed to enhance productivity and security by storing state-files as `base64` encoded strings in a secure location. It streamlines the encryption and storage of state files across multiple platforms, including `HashiCorp Vault`, `Azure Storage Account`, and soon, `AWS S3 buckets`. By automating the encryption and push/pull processes, Vaultify ensures your Terraform state files are securely managed and easily accessible.

> NOTE: You can also refer to vaultify documentation at [Vaultify](https://vaultify.buungroup.com) to learn more.
Expand Down Expand Up @@ -382,4 +382,4 @@ This section covers how to contrinute to this project see the [CONTRIBUTING](CO

# 📃 License

This project is licensed under the `GNU General Public License, Version 3 (GPL-3.0)` - see the [LICENSE](LICENSE) file for details.
This project is licensed under the `GNU General Public License, Version 3 (GPL-3.0)` - see the [LICENSE](LICENSE) file for details.
270 changes: 134 additions & 136 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ package cmd

import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/Azure/azure-sdk-for-go/storage"
"io"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"net/http"
"io"
"github.com/Azure/azure-sdk-for-go/storage"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
)

// ###############################
Expand Down Expand Up @@ -71,24 +71,24 @@ func getCurrentWorkspace() (string, error) {
// #########################

func readSettings() (*Configuration, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("❌ Error getting user home directory: %v", err)
}

settingsFilePath := filepath.Join(homeDir, ".vaultify", "settings.json")
content, err := os.ReadFile(settingsFilePath)
if err != nil {
return nil, fmt.Errorf("❌ Error reading settings file: \033[33m%v\033[0m", err)
}

var config Configuration
err = json.Unmarshal(content, &config)
if err != nil {
return nil, fmt.Errorf("❌ Error unmarshalling settings JSON: \033[33m%v\033[0m", err)
}

return &config, nil
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("❌ Error getting user home directory: %v", err)
}

settingsFilePath := filepath.Join(homeDir, ".vaultify", "settings.json")
content, err := os.ReadFile(settingsFilePath)
if err != nil {
return nil, fmt.Errorf("❌ Error reading settings file: \033[33m%v\033[0m", err)
}

var config Configuration
err = json.Unmarshal(content, &config)
if err != nil {
return nil, fmt.Errorf("❌ Error unmarshalling settings JSON: \033[33m%v\033[0m", err)
}

return &config, nil
}

// #####################################
Expand Down Expand Up @@ -252,10 +252,9 @@ func deleteVolume(volumeName string) {
// ####################

type OAuthResponse struct {
AccessToken string `json:"access_token"`
AccessToken string `json:"access_token"`
}


type Subscription struct {
ID string `json:"id"`
DisplayName string `json:"displayName"`
Expand All @@ -265,7 +264,6 @@ type SubscriptionsResponse struct {
Subscriptions []Subscription `json:"value"`
}


func AuthenticateWithAzureAD() (string, error) {
tenantID := os.Getenv("ARM_TENANT_ID")
clientID := os.Getenv("ARM_CLIENT_ID")
Expand Down Expand Up @@ -303,138 +301,138 @@ func AuthenticateWithAzureAD() (string, error) {
}

func checkAzureStorageAccountExists() (bool, error) {
accessToken, err := AuthenticateWithAzureAD()
if err != nil {
return false, fmt.Errorf("error obtaining access token: %v", err)
}

config, err := readConfiguration()
if err != nil {
return false, fmt.Errorf("error reading configuration: %v", err)
}

accountName := config.Settings.Azure.StorageAccountName
resourceGroup := config.Settings.Azure.StorageAccountResourceGroupName
subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID")

if subscriptionID == "" {
return false, fmt.Errorf("subscription ID is missing")
}

url := fmt.Sprintf("https://management.azure.com/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s?api-version=2019-06-01", subscriptionID, resourceGroup, accountName)

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return false, err
}

req.Header.Set("Authorization", "Bearer "+accessToken)

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return false, err
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
return true, nil
} else {
bodyBytes, _ := io.ReadAll(resp.Body)
return false, fmt.Errorf("storage account check failed with status %d: %s", resp.StatusCode, string(bodyBytes))
}
accessToken, err := AuthenticateWithAzureAD()
if err != nil {
return false, fmt.Errorf("error obtaining access token: %v", err)
}

config, err := readConfiguration()
if err != nil {
return false, fmt.Errorf("error reading configuration: %v", err)
}

accountName := config.Settings.Azure.StorageAccountName
resourceGroup := config.Settings.Azure.StorageAccountResourceGroupName
subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID")

if subscriptionID == "" {
return false, fmt.Errorf("subscription ID is missing")
}

url := fmt.Sprintf("https://management.azure.com/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Storage/storageAccounts/%s?api-version=2019-06-01", subscriptionID, resourceGroup, accountName)

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return false, err
}

req.Header.Set("Authorization", "Bearer "+accessToken)

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return false, err
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
return true, nil
} else {
bodyBytes, _ := io.ReadAll(resp.Body)
return false, fmt.Errorf("storage account check failed with status %d: %s", resp.StatusCode, string(bodyBytes))
}
}

func CheckAzureEnvVars() error {
config, err := readConfiguration()
if err != nil {
return fmt.Errorf("error reading configuration: %v", err)
}

if config.Settings.DefaultSecretStorage == "azure_storage" {
requiredEnvVars := []string{
"ARM_SUBSCRIPTION_ID",
"ARM_CLIENT_ID",
"ARM_CLIENT_SECRET",
"ARM_TENANT_ID",
}

var missingVars []string
for _, envVar := range requiredEnvVars {
if os.Getenv(envVar) == "" {
missingVars = append(missingVars, envVar)
}
}

if len(missingVars) > 0 {
return fmt.Errorf("missing required environment variables for Azure storage: \033[33m%v\033[0m", missingVars)
}
}

return nil
config, err := readConfiguration()
if err != nil {
return fmt.Errorf("error reading configuration: %v", err)
}

if config.Settings.DefaultSecretStorage == "azure_storage" {
requiredEnvVars := []string{
"ARM_SUBSCRIPTION_ID",
"ARM_CLIENT_ID",
"ARM_CLIENT_SECRET",
"ARM_TENANT_ID",
}

var missingVars []string
for _, envVar := range requiredEnvVars {
if os.Getenv(envVar) == "" {
missingVars = append(missingVars, envVar)
}
}

if len(missingVars) > 0 {
return fmt.Errorf("missing required environment variables for Azure storage: \033[33m%v\033[0m", missingVars)
}
}

return nil
}

// ####################################################
// # Create Vaultify Container inside Storage Account #
// ####################################################

func createContainer(accountName, key string) {
containerName := "vaultify"
containerName := "vaultify"

client, err := storage.NewBasicClient(accountName, key)
if err != nil {
fmt.Println("Error creating storage client:\033[33m", err)
return
}
client, err := storage.NewBasicClient(accountName, key)
if err != nil {
fmt.Println("Error creating storage client:\033[33m", err)
return
}

blobClient := client.GetBlobService()
blobClient := client.GetBlobService()

container := blobClient.GetContainerReference(containerName)
container := blobClient.GetContainerReference(containerName)

exists, err := container.Exists()
if err != nil {
fmt.Println("Error checking container existence:\033[33m", err)
return
}
exists, err := container.Exists()
if err != nil {
fmt.Println("Error checking container existence:\033[33m", err)
return
}

if exists {
if exists {

} else {
err := container.Create(nil)
if err != nil {
fmt.Println("Failed to create container:\033[33m", err)
return
}
} else {
err := container.Create(nil)
if err != nil {
fmt.Println("Failed to create container:\033[33m", err)
return
}

fmt.Println("Container \033[33m'vaultify'\033[0m created successfully.")
}
fmt.Println("Container \033[33m'vaultify'\033[0m created successfully.")
}
}

func generateSignature(accountName, accountKey, method, contentLength, contentType, date, blobType, containerName, blobName string) (string, error) {
urlPath := fmt.Sprintf("/%s/%s/%s", accountName, containerName, blobName)
urlPath := fmt.Sprintf("/%s/%s/%s", accountName, containerName, blobName)

stringToSign := method + "\n"
stringToSign := method + "\n"

if method == "PUT" {
stringToSign += "\n\n" + contentLength + "\n\n" + contentType + "\n\n\n\n\n\n\n"
} else {
stringToSign += "\n\n\n\n\n\n\n\n\n\n\n"
}
if method == "PUT" {
stringToSign += "\n\n" + contentLength + "\n\n" + contentType + "\n\n\n\n\n\n\n"
} else {
stringToSign += "\n\n\n\n\n\n\n\n\n\n\n"
}

if method == "PUT" {
stringToSign += "x-ms-blob-type:" + blobType + "\n"
}
stringToSign += "x-ms-date:" + date + "\n" + "x-ms-version:2019-12-12\n" + urlPath
if method == "PUT" {
stringToSign += "x-ms-blob-type:" + blobType + "\n"
}
stringToSign += "x-ms-date:" + date + "\n" + "x-ms-version:2019-12-12\n" + urlPath

key, err := base64.StdEncoding.DecodeString(accountKey)
if err != nil {
return "", fmt.Errorf("error decoding storage account access key: %v", err)
}
key, err := base64.StdEncoding.DecodeString(accountKey)
if err != nil {
return "", fmt.Errorf("error decoding storage account access key: %v", err)
}

hasher := hmac.New(sha256.New, key)
hasher.Write([]byte(stringToSign))
signature := base64.StdEncoding.EncodeToString(hasher.Sum(nil))
hasher := hmac.New(sha256.New, key)
hasher.Write([]byte(stringToSign))
signature := base64.StdEncoding.EncodeToString(hasher.Sum(nil))

authHeader := fmt.Sprintf("SharedKey %s:%s", accountName, signature)
return authHeader, nil
}
authHeader := fmt.Sprintf("SharedKey %s:%s", accountName, signature)
return authHeader, nil
}
Loading

0 comments on commit 8978da9

Please sign in to comment.