Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add vault's k8s authentication support #16

Merged
merged 3 commits into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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