Skip to content

Commit

Permalink
Merge pull request #16 from luanphantiki/master
Browse files Browse the repository at this point in the history
add vault's k8s authentication support
  • Loading branch information
Lucretius authored Sep 27, 2021
2 parents 7901329 + 74d7799 commit 0003c4d
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 17 deletions.
26 changes: 26 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM golang:1.16 AS builder

ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64

RUN mkdir /build
WORKDIR /build

COPY . .

RUN go mod download
RUN go build \
-a \
-trimpath \
-ldflags "-s -w -extldflags '-static'" \
-tags 'osusergo netgo static_build' \
-o ../vault_raft_snapshot_agent \
./main.go

FROM alpine
WORKDIR /
COPY --from=builder /vault_raft_snapshot_agent .
COPY snapshot.json /etc/vault.d/snapshot.json
ENTRYPOINT ["/vault_raft_snapshot_agent"]
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,25 @@ If your configuration is right and Vault is running on the same host as the agen

`frequency` How often to run the snapshot agent. Examples: `30s`, `1h`. See https://golang.org/pkg/time/#ParseDuration for a full list of valid time units.


### Default authentication mode
`role_id` Specifies the role_id used to call the Vault API. See the authentication steps below.

`secret_id` Specifies the secret_id used to call the Vault API.

`approle` Specifies the approle name used to login. Defaults to "approle".


### Kubernetes authentication mode
Incase we're running the application under kubernetes, we can use Vault's Kubernetes Auth
as below. Read more on [kubernetes auth mode](https://www.vaultproject.io/docs/auth/kubernetes)

`vault_auth_method` Set it to "k8s", otherwise, approle will be chosen

`vault_auth_role` Specifies vault k8s auth role

`vault_auth_path` Specifies vault k8s auth path

### Storage options

Note that if you specify more than one storage option, *all* options will be written to. For example, specifying `local_storage` and `aws_storage` will write to both locations.
Expand Down Expand Up @@ -118,6 +131,9 @@ Note that if you specify more than one storage option, *all* options will be wri

## Authentication


### Default authentication mode

You must do some quick initial setup prior to being able to use the Snapshot Agent. This involves the following:

`vault login` with an admin user.
Expand All @@ -143,3 +159,8 @@ and copy your secret and role ids, and place them into the snapshot file. The s
The AppRole allows the snapshot agent to automatically rotate tokens to avoid long-lived credentials.

To learn more about AppRole's and why this project chose to use them, see [the Vault docs](https://www.vaultproject.io/docs/auth/approle)


### Kubernetes authentication mode

To Enable Kubernetes authentication mode, we should follow these steps from [the Vault docs](https://www.vaultproject.io/docs/auth/kubernetes#configuration)
23 changes: 13 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ import (

// Configuration is the overall config object
type Configuration struct {
Address string `json:"addr"`
Retain int64 `json:"retain"`
Frequency string `json:"frequency"`
AWS S3Config `json:"aws_storage"`
Local LocalConfig `json:"local_storage"`
GCP GCPConfig `json:"google_storage"`
Azure AzureConfig `json:"azure_storage"`
RoleID string `json:"role_id"`
SecretID string `json:"secret_id"`
Approle string `json:"approle"`
Address string `json:"addr"`
Retain int64 `json:"retain"`
Frequency string `json:"frequency"`
AWS S3Config `json:"aws_storage"`
Local LocalConfig `json:"local_storage"`
GCP GCPConfig `json:"google_storage"`
Azure AzureConfig `json:"azure_storage"`
RoleID string `json:"role_id"`
SecretID string `json:"secret_id"`
Approle string `json:"approle"`
K8sAuthRole string `json:"k8s_auth_role,omitempty"`
K8sAuthPath string `json:"k8s_auth_path,omitempty"`
VaultAuthMethod string `json:"vault_auth_method,omitempty"`
}

// AzureConfig is the configuration for Azure blob snapshots
Expand Down
9 changes: 7 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,20 @@ func main() {

for {
if snapshotter.TokenExpiration.Before(time.Now()) {
snapshotter.SetClientTokenFromAppRole(c)
switch c.VaultAuthMethod {
case "k8s":
snapshotter.SetClientTokenFromK8sAuth(c)
default:
snapshotter.SetClientTokenFromAppRole(c)
}
}
leader, err := snapshotter.API.Sys().Leader()
if err != nil {
log.Println(err.Error())
log.Fatalln("Unable to determine leader instance. The snapshot agent will only run on the leader node. Are you running this daemon on a Vault instance?")
}
leaderIsSelf := leader.IsSelf
if ! leaderIsSelf {
if !leaderIsSelf {
log.Println("Not running on leader node, skipping.")
} else {
var snapshot bytes.Buffer
Expand Down
18 changes: 18 additions & 0 deletions snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"addr":"http://vaul-addr:8200",
"retain":72,
"frequency":"3600s",
"role_id": "<vault app role id>",
"secret_id":"<vault app secret id",
"vault_auth_method": "",
"vault_auth_role": "",
"vault_auth_path": "",
"aws_storage":{
"access_key_id":"<s3 access id>",
"secret_access_key":"<s3 acess key>",
"s3_region":"<s3 region>",
"s3_bucket":"<s3 bucket",
"s3_endpoint":"<s3 endpoint>",
"s3_force_path_style":true
}
}
47 changes: 42 additions & 5 deletions snapshot_agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log"
"net/url"
"os"
"path"
"time"

"cloud.google.com/go/storage"
Expand Down Expand Up @@ -70,11 +71,10 @@ func (s *Snapshotter) ConfigureVaultClient(config *config.Configuration) error {
return err
}
s.API = api
err = s.SetClientTokenFromAppRole(config)
if err != nil {
return err
if config.VaultAuthMethod == "k8s" {
return s.SetClientTokenFromK8sAuth(config)
}
return nil
return s.SetClientTokenFromAppRole(config)
}

func (s *Snapshotter) SetClientTokenFromAppRole(config *config.Configuration) error {
Expand All @@ -86,7 +86,7 @@ func (s *Snapshotter) SetClientTokenFromAppRole(config *config.Configuration) er
if config.Approle != "" {
approle = config.Approle
}
resp, err := s.API.Logical().Write("auth/" + approle + "/login", data)
resp, err := s.API.Logical().Write("auth/"+approle+"/login", data)
if err != nil {
return fmt.Errorf("error logging into AppRole auth backend: %s", err)
}
Expand All @@ -95,6 +95,43 @@ func (s *Snapshotter) SetClientTokenFromAppRole(config *config.Configuration) er
return nil
}

func (s *Snapshotter) SetClientTokenFromK8sAuth(config *config.Configuration) error {

if config.K8sAuthPath == "" || config.K8sAuthRole == "" {
return errors.New("missing k8s auth definitions")
}

jwt, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
return err
}
data := map[string]string{
"role": config.K8sAuthRole,
"jwt": string(jwt),
}

login := path.Clean("/v1/auth/" + config.K8sAuthPath + "/login")
req := s.API.NewRequest("POST", login)
req.SetJSONBody(data)

resp, err := s.API.RawRequest(req)
if err != nil {
return err
}
if respErr := resp.Error(); respErr != nil {
return respErr
}

var result vaultApi.Secret
if err := resp.DecodeJSON(&result); err != nil {
return err
}

s.API.SetToken(result.Auth.ClientToken)
s.TokenExpiration = time.Now().Add(time.Duration((time.Second * time.Duration(result.Auth.LeaseDuration)) / 2))
return nil
}

func (s *Snapshotter) ConfigureS3(config *config.Configuration) error {
awsConfig := &aws.Config{Region: aws.String(config.AWS.Region)}

Expand Down

0 comments on commit 0003c4d

Please sign in to comment.