Skip to content
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
20 changes: 10 additions & 10 deletions .github/actions/bootstrap-integration-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ runs:
using: "composite"
steps:
- run: |
echo "Install Fastly CLI 3.0.0"
wget https://github.com/fastly/cli/releases/download/v3.0.0/fastly_3.0.0_linux_amd64.deb
sudo apt install ./fastly_3.0.0_linux_amd64.deb
echo "Install Fastly CLI 5.0.0"
wget https://github.com/fastly/cli/releases/download/v5.0.0/fastly_5.0.0_linux_amd64.deb
sudo apt install ./fastly_5.0.0_linux_amd64.deb
shell: "bash"
- run: |
echo "Install Viceroy 0.2.14..."
wget https://github.com/fastly/Viceroy/releases/download/v0.2.14/viceroy_v0.2.14_linux-amd64.tar.gz
echo "Install Viceroy 0.3.5..."
wget https://github.com/fastly/Viceroy/releases/download/v0.3.5/viceroy_v0.3.5_linux-amd64.tar.gz
mkdir -p $HOME/bin
tar -xzf viceroy_v0.2.14_linux-amd64.tar.gz --directory $HOME/bin
tar -xzf viceroy_v0.3.5_linux-amd64.tar.gz --directory $HOME/bin
echo "$HOME/bin" >> $GITHUB_PATH
shell: "bash"
- run: |
echo "Install TinyGo 0.24.0..."
wget https://github.com/tinygo-org/tinygo/releases/download/v0.24.0/tinygo_0.24.0_amd64.deb
echo "f9366fafa4b281a6874f17017053f9223a2222521c2084f50cc323a976f8f34848a13a31b4d60ddd24a3ab04d56ae5fd8c9445aa2b42245bbb3037c894452ae1 tinygo_0.24.0_amd64.deb" | sha512sum --check
sudo dpkg -i tinygo_0.24.0_amd64.deb
echo "Install TinyGo 0.26.0..."
wget https://github.com/tinygo-org/tinygo/releases/download/v0.26.0/tinygo_0.26.0_amd64.deb
echo "dd7d47bc15be3690932394faca93ac6433a309cbe2957fc71e5688e567cd6d1ce0b8bf1e92455058e4547db6a70e176af85fe1b507537966a160bf88a819a101 tinygo_0.26.0_amd64.deb" | sha512sum --check
sudo dpkg -i tinygo_0.26.0_amd64.deb
echo "/usr/local/tinygo/bin" >> $GITHUB_PATH
shell: "bash"
4 changes: 2 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18'
go-version: '1.19'
- name: compute-sdk-go Integration Tests Job
uses: ./.github/actions/compute-sdk-test
id: sdktest
Expand All @@ -42,7 +42,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.17'
go-version: '1.18'
- name: compute-sdk-go Integration Tests Job
uses: ./.github/actions/compute-sdk-test
id: sdktest
Expand Down
42 changes: 42 additions & 0 deletions _examples/secret-store/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"context"
"errors"
"fmt"

"github.com/fastly/compute-sdk-go/fsthttp"
"github.com/fastly/compute-sdk-go/secretstore"
)

func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
st, err := secretstore.Open("example_secretstore")
switch {
case errors.Is(err, secretstore.ErrSecretStoreNotFound):
fsthttp.Error(w, err.Error(), fsthttp.StatusNotFound)
return
case err != nil:
fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
return
}

s, err := st.Get("my_secret")
switch {
case errors.Is(err, secretstore.ErrSecretNotFound):
fsthttp.Error(w, err.Error(), fsthttp.StatusNotFound)
return
case err != nil:
fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
return
}

v, err := s.Plaintext()
if err != nil {
fsthttp.Error(w, err.Error(), fsthttp.StatusBadGateway)
return
}

fmt.Fprintf(w, "secret value: %q", v)
})
}
12 changes: 12 additions & 0 deletions _integration_tests/fixtures/secret_store/fastly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This file describes a Fastly Compute@Edge package. To learn more visit:
# https://developer.fastly.com/reference/fastly-toml/

authors = ["oss@fastly.com"]
description = ""
language = "go"
manifest_version = 2
name = "secret_store"
service_id = ""

[local_server]
secret_store.phrases = [{key = "my_phrase", data = "sssh! don't tell anyone!"}]
41 changes: 41 additions & 0 deletions _integration_tests/fixtures/secret_store/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"context"
"errors"

"github.com/fastly/compute-sdk-go/fsthttp"
"github.com/fastly/compute-sdk-go/secretstore"
)

func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
st, err := secretstore.Open("phrases")
switch {
case errors.Is(err, secretstore.ErrSecretStoreNotFound):
fsthttp.Error(w, err.Error(), fsthttp.StatusNotFound)
return
case err != nil:
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
return
}

s, err := st.Get("my_phrase")
switch {
case errors.Is(err, secretstore.ErrSecretNotFound):
fsthttp.Error(w, err.Error(), fsthttp.StatusNotFound)
return
case err != nil:
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
return
}

v, err := s.Plaintext()
if err != nil {
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
return
}

w.Write(v)
})
}
21 changes: 21 additions & 0 deletions _integration_tests/sdk-test-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,27 @@
]
}
}
},

"secret_store" : {
"build": "tinygo build -target=wasi -scheduler=asyncify -gc=leaking -wasm-abi=generic -o ./_integration_tests/fixtures/secret_store/secret_store.wasm ./_integration_tests/fixtures/secret_store && (cd ./_integration_tests/fixtures/secret_store && fastly compute pack --verbose --wasm-binary ./secret_store.wasm)",
"fastly_toml_path": "./_integration_tests/fixtures/secret_store/fastly.toml",
"wasm_path": "./_integration_tests/fixtures/secret_store/secret_store.wasm",
"pkg_path": "./_integration_tests/fixtures/secret_store/pkg/secret-store.tar.gz",
"tests": {
"POST /": {
"environments": ["viceroy"],
"downstream_request": {
"method": "GET",
"pathname": "/"
},
"downstream_response": {
"status": 200,
"body": "sssh! don't tell anyone!"
}
}
}
}

}
}
134 changes: 132 additions & 2 deletions internal/abi/fastly/hostcalls_guest.go
Original file line number Diff line number Diff line change
Expand Up @@ -2105,7 +2105,6 @@ func fastlyObjectStoreLookup(

// Lookup returns the value for key, if it exists.
func (o *ObjectStore) Lookup(key string) (io.Reader, error) {

body := HTTPBody{h: invalidBodyHandle}

if err := fastlyObjectStoreLookup(
Expand Down Expand Up @@ -2145,7 +2144,6 @@ func fastlyObjectStoreInsert(

// Insert adds a key/value pair to the object store.
func (o *ObjectStore) Insert(key string, value io.Reader) error {

body, err := NewHTTPBody()
if err != nil {
return err
Expand All @@ -2165,3 +2163,135 @@ func (o *ObjectStore) Insert(key string, value io.Reader) error {

return nil
}

// SecretStore represents a Fastly secret store, a collection of
// key/value pairs for storing sensitive data.
type SecretStore struct {
h secretStoreHandle
}

// Secret represents a secret value. Data is encrypted at rest, and is
// only decrypted upon the first call to the secret's Plaintext method.
type Secret struct {
h secretHandle
}

// witx:
//
// (module $fastly_secret_store
// (@interface func (export "open")
// (param $name string)
// (result $err (expected $secret_store_handle (error $fastly_status)))
// )

//go:wasm-module fastly_secret_store
//export open
//go:noescape
func fastlySecretStoreOpen(
name prim.Wstring,
h *secretStoreHandle,
) FastlyStatus

// OpenSecretStore returns a reference to the named secret store, if it exists.
func OpenSecretStore(name string) (*SecretStore, error) {
var st SecretStore

if err := fastlySecretStoreOpen(
prim.NewReadBufferFromString(name).Wstring(),
&st.h,
).toError(); err != nil {
return nil, err
}

return &st, nil
}

// witx:
//
// (module $fastly_secret_store
// (@interface func (export "get")
// (param $store $secret_store_handle)
// (param $key string)
// (result $err (expected $secret_handle (error $fastly_status)))
// )
// )

//go:wasm-module fastly_secret_store
//export get
//go:noescape
func fastlySecretStoreGet(
h secretStoreHandle,
key prim.Wstring,
s *secretHandle,
) FastlyStatus

// Get returns a handle to the secret value for the given name, if it
// exists.
func (st *SecretStore) Get(name string) (*Secret, error) {
var s Secret

if err := fastlySecretStoreGet(
st.h,
prim.NewReadBufferFromString(name).Wstring(),
&s.h,
).toError(); err != nil {
return nil, err
}

return &s, nil
}

// witx:
//
// (module $fastly_secret_store
// (@interface func (export "plaintext")
// (param $secret $secret_handle)
// (param $buf (@witx pointer (@witx char8)))
// (param $buf_len (@witx usize))
// (param $nwritten_out (@witx pointer (@witx usize)))
// (result $err (expected (error $fastly_status)))
// )
// )

//go:wasm-module fastly_secret_store
//export plaintext
//go:noescape
func fastlySecretPlaintext(
h secretHandle,
buf *prim.Char8,
bufLen prim.Usize,
nwritten *prim.Usize,
) FastlyStatus

// Plaintext decrypts and returns the secret value as a byte slice.
func (s *Secret) Plaintext() ([]byte, error) {
// Most secrets will fit into the initial secret buffer size, so
// we'll start with that. If it doesn't fit, we'll know the exact
// size of the buffer to try again.
buf := prim.NewWriteBuffer(InitialSecretLen)

status := fastlySecretPlaintext(
s.h,
buf.Char8Pointer(),
buf.Cap(),
buf.NPointer(),
)
if status == FastlyStatusBufLen {
// The buffer was too small, but it'll tell us how big it will
// need to be in order to fit the plaintext.
buf = prim.NewWriteBuffer(int(buf.NValue()))

status = fastlySecretPlaintext(
s.h,
buf.Char8Pointer(),
buf.Cap(),
buf.NPointer(),
)
}

if err := status.toError(); err != nil {
return nil, err
}

return buf.AsBytes(), nil
}
17 changes: 17 additions & 0 deletions internal/abi/fastly/hostcalls_noguest.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,20 @@ func (o *ObjectStore) Lookup(key string) (io.Reader, error) {
func (o *ObjectStore) Insert(key string, value io.Reader) error {
return fmt.Errorf("not implemented")
}

type (
SecretStore struct{}
Secret struct{}
)

func OpenSecretStore(name string) (*SecretStore, error) {
return nil, fmt.Errorf("not implemented")
}

func (s *SecretStore) Get(name string) (*Secret, error) {
return nil, fmt.Errorf("not implemented")
}

func (s *Secret) Plaintext() ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}
9 changes: 9 additions & 0 deletions internal/abi/fastly/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ var (
MaxMethodLen = 1024
// MaxURLLen is the url limit
MaxURLLen = 1024
// InitialSecretLen is the initial size of the buffer to use for decrypting secrets
InitialSecretLen = 1024
)

const (
Expand Down Expand Up @@ -470,3 +472,10 @@ const (
//
// (typename $object_store_handle (handle))
type objectStoreHandle handle

// witx:
//
// (typename $secret_store_handle (handle))
// (typename $secret_handle (handle))
type secretStoreHandle handle
type secretHandle handle
Loading