Skip to content

Commit

Permalink
add configFile listener; ETag support
Browse files Browse the repository at this point in the history
  • Loading branch information
salrashid123 committed Mar 21, 2024
1 parent b168a5c commit 4b48ff9
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 60 deletions.
53 changes: 37 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ r.Handle("/")
- [Run with containers](#run-with-containers)
- [Running as Kubernetes Service](#running-as-kubernetes-service)
- [Static environment variables](#static-environment-variables)
- [Dynamic Configuration File Updates](#dynamic-configuration-file-updates)
- [ETag](#etag)
- [Extending the sample](#extending-the-sample)
- [Using link-local address](#using-link-local-address)
- [Using domain sockets](#using-domain-sockets)
Expand Down Expand Up @@ -608,22 +610,7 @@ This emulator is also published as a release-tagged container to dockerhub:

* [https://hub.docker.com/r/salrashid123/gcemetadataserver](https://hub.docker.com/r/salrashid123/gcemetadataserver)

The images are also signed using my github address (`salrashid123@gmail`). If you really want to, you can verify each signature usign `cosign`:

```bash
## for tag/version 3.4.0:
IMAGE="index.docker.io/salrashid123/gcemetadataserver@sha256:c3cec9e18adb87a14889f19ab0c3c87d66339284b35ca72135ff9dcd58a59671"

## i signed it directly, keyless:
# $ cosign sign $IMAGE

## which you can verify:
$ cosign verify [email protected] --certificate-oidc-issuer=https://github.com/login/oauth $IMAGE | jq '.'

## search and get
# $ rekor-cli search --rekor_server https://rekor.sigstore.dev --email [email protected]
# $ rekor-cli get --rekor_server https://rekor.sigstore.dev --log-index $LogIndex --format=json | jq '.'
```
You can verify the image were signed by the repo owner if you really want to (see section below).

### Run with containers

Expand Down Expand Up @@ -696,6 +683,21 @@ Number of Buckets: 62
>> needless to say, the metadata Service should be accessed only form authorized pods
### Dynamic Configuration File Updates
Changes to the claims configuration file (`--configFile=`) while the metadata server is running will automatically update values returned by the server.

On startup, the metadata server sets a file listener on that config file and any updates to the values will propagate back to the server without requiring a restart.

### ETag

GCE metadata servers return values with [ETag](https://cloud.google.com/compute/docs/metadata/querying-metadata#etags) headers. The ETag is used to check if a specific attribute or value has changed.

This metadata server will hash the value for the body to return and use that as the ETag. If you update the configuration file with new attributes or values, the ETag for that node will change. The `ETag` header key is returned in non-canonical format.

Note `wait-for-change` value is not supported currently so while you can poll for etag changes, you cannot listen and hold.


### Static environment variables

If you do not have access to certificate file or would like to specify **static** token values via env-var, the metadata server supports the following environment variables as substitutions. Once you set these environment variables, the service will not look for anything using the service Account JSON file (even if specified)
Expand Down Expand Up @@ -973,6 +975,25 @@ wget https://github.com/salrashid123/gce_metadata_server/releases/download/v3.4.
gpg --verify gce_metadata_server_3.4.1_checksums.txt.sig gce_metadata_server_3.4.1_checksums.txt
```

#### Verify Container Image Signature

The images are also signed using my github address (`salrashid123@gmail`). If you really want to, you can verify each signature usign `cosign`:

```bash
## for tag/version 3.4.0:
IMAGE="index.docker.io/salrashid123/gcemetadataserver@sha256:c3cec9e18adb87a14889f19ab0c3c87d66339284b35ca72135ff9dcd58a59671"

## i signed it directly, keyless:
# $ cosign sign $IMAGE

## which you can verify:
$ cosign verify [email protected] --certificate-oidc-issuer=https://github.com/login/oauth $IMAGE | jq '.'

## search and get
# $ rekor-cli search --rekor_server https://rekor.sigstore.dev --email [email protected]
# $ rekor-cli get --rekor_server https://rekor.sigstore.dev --log-index $LogIndex --format=json | jq '.'
```

## Testing

a lot todo here, right...thats just life
Expand Down
1 change: 1 addition & 0 deletions cmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ go_library(
"@com_google_cloud_go_iam//credentials/apiv1:go_default_library",
"@com_github_google_go_tpm//legacy/tpm2:go_default_library",
"@com_github_golang_glog//:go_default_library",
"@com_github_fsnotify_fsnotify//:go_default_library",
],
)

Expand Down
51 changes: 51 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import (
"flag"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"

"github.com/fsnotify/fsnotify"
"github.com/golang/glog"
"github.com/google/go-tpm/legacy/tpm2"
mds "github.com/salrashid123/gce_metadata_server"
Expand Down Expand Up @@ -156,6 +159,54 @@ func main() {
os.Exit(1)
}

watcher, err := fsnotify.NewWatcher()
if err != nil {
glog.Errorf("Error creating file watcher: %v\n", err)
os.Exit(1)
}
defer watcher.Close()

go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}

if event.Has(fsnotify.Write) {
if event.Name == *configFile {
time.Sleep(8 * time.Millisecond) // https://github.com/fsnotify/fsnotify/issues/372
configData, err := os.ReadFile(*configFile)
if err != nil {
glog.Errorf("Error reading configFile: %v\n", err)
return
}

claims := &mds.Claims{}
err = json.Unmarshal(configData, claims)
if err != nil {
glog.Errorf("Error parsing json: %v\n", err)
return
}
f.Claims = *claims
}
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
glog.Errorf("Error on filewatcher %v\n", err)
}
}
}()

err = watcher.Add(filepath.Dir(*configFile))
if err != nil {
glog.Errorf("Error watching configFile: %v\n", err)
os.Exit(1)
}

done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.20
require (
cloud.google.com/go/compute/metadata v0.2.3
cloud.google.com/go/iam v1.1.5
github.com/fsnotify/fsnotify v1.7.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/glog v1.2.0
github.com/google/go-tpm v0.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
Expand Down
9 changes: 8 additions & 1 deletion repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ def go_repositories():
sum = "h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=",
version = "v1.0.4",
)
go_repository(
name = "com_github_fsnotify_fsnotify",
importpath = "github.com/fsnotify/fsnotify",
sum = "h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=",
version = "v1.7.0",
)

go_repository(
name = "com_github_go_logr_logr",
importpath = "github.com/go-logr/logr",
Expand Down Expand Up @@ -201,8 +208,8 @@ def go_repositories():
)
go_repository(
name = "com_github_googleapis_gax_go_v2",
build_file_proto_mode = "disable_global",
importpath = "github.com/googleapis/gax-go/v2",
build_file_proto_mode = "disable_global",
sum = "h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=",
version = "v2.12.0",
)
Expand Down
Loading

0 comments on commit 4b48ff9

Please sign in to comment.