Skip to content

Commit

Permalink
fix: prevent server side request forgery using Kubernetes storage
Browse files Browse the repository at this point in the history
Signed-off-by: m.nabokikh <maksim.nabokikh@flant.com>
  • Loading branch information
nabokihms committed Dec 23, 2022
1 parent 54345b6 commit 0d20d24
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 10 deletions.
48 changes: 39 additions & 9 deletions storage/kubernetes/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"net/url"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -83,14 +84,26 @@ func offlineTokenName(userID string, connID string, h func() hash.Hash) string {
return strings.TrimRight(encoding.EncodeToString(hash.Sum(nil)), "=")
}

const kubeResourceMaxLen = 63

var kubeResourceNameRegex = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)

func (cli *client) urlForWithParams(
apiVersion, namespace, resource, name string, params url.Values,
) string {
) (string, error) {
basePath := "apis/"
if apiVersion == "v1" {
basePath = "api/"
}

if name != "" && (len(name) > kubeResourceMaxLen || !kubeResourceNameRegex.MatchString(name)) {
// The actual name can be found in auth request or auth code objects and equals to the state value
return "", fmt.Errorf(
"invalid kubernetes resource name: must match the pattern %s and be no longer than %d charactes",
kubeResourceNameRegex.String(),
kubeResourceMaxLen)
}

var p string
if namespace != "" {
p = path.Join(basePath, apiVersion, "namespaces", namespace, resource, name)
Expand All @@ -105,13 +118,13 @@ func (cli *client) urlForWithParams(
}

if strings.HasSuffix(cli.baseURL, "/") {
return cli.baseURL + p + paramsSuffix
return cli.baseURL + p + paramsSuffix, nil
}

return cli.baseURL + "/" + p + paramsSuffix
return cli.baseURL + "/" + p + paramsSuffix, nil
}

func (cli *client) urlFor(apiVersion, namespace, resource, name string) string {
func (cli *client) urlFor(apiVersion, namespace, resource, name string) (string, error) {
return cli.urlForWithParams(apiVersion, namespace, resource, name, url.Values{})
}

Expand Down Expand Up @@ -191,13 +204,20 @@ func (cli *client) getURL(url string, v interface{}) error {
}

func (cli *client) getResource(apiVersion, namespace, resource, name string, v interface{}) error {
return cli.getURL(cli.urlFor(apiVersion, namespace, resource, name), v)
u, err := cli.urlFor(apiVersion, namespace, resource, name)
if err != nil {
return err
}
return cli.getURL(u, v)
}

func (cli *client) listN(resource string, v interface{}, n int) error {
params := url.Values{}
params.Add("limit", fmt.Sprintf("%d", n))
u := cli.urlForWithParams(cli.apiVersion, cli.namespace, resource, "", params)
u, err := cli.urlForWithParams(cli.apiVersion, cli.namespace, resource, "", params)
if err != nil {
return err
}
return cli.getURL(u, v)
}

Expand All @@ -215,7 +235,10 @@ func (cli *client) postResource(apiVersion, namespace, resource string, v interf
return fmt.Errorf("marshal object: %v", err)
}

url := cli.urlFor(apiVersion, namespace, resource, "")
url, err := cli.urlFor(apiVersion, namespace, resource, "")
if err != nil {
return err
}
resp, err := cli.client.Post(url, "application/json", bytes.NewReader(body))
if err != nil {
return err
Expand Down Expand Up @@ -256,7 +279,10 @@ func (cli *client) detectKubernetesVersion() error {
}

func (cli *client) delete(resource, name string) error {
url := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
url, err := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
if err != nil {
return err
}
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
return fmt.Errorf("create delete request: %v", err)
Expand Down Expand Up @@ -295,7 +321,11 @@ func (cli *client) put(resource, name string, v interface{}) error {
return fmt.Errorf("marshal object: %v", err)
}

url := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
url, err := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
if err != nil {
return err
}

req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
if err != nil {
return fmt.Errorf("create patch request: %v", err)
Expand Down
6 changes: 5 additions & 1 deletion storage/kubernetes/storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ func TestURLFor(t *testing.T) {

for _, test := range tests {
c := &client{baseURL: test.baseURL}
got := c.urlFor(test.apiVersion, test.namespace, test.resource, test.name)
got, err := c.urlFor(test.apiVersion, test.namespace, test.resource, test.name)
if err != nil {
t.Errorf("got error: %v", err)
}

if got != test.want {
t.Errorf("(&client{baseURL:%q}).urlFor(%q, %q, %q, %q): expected %q got %q",
test.baseURL,
Expand Down

0 comments on commit 0d20d24

Please sign in to comment.