Skip to content

Commit

Permalink
Merge branch 'feature/secure-control-api'
Browse files Browse the repository at this point in the history
  • Loading branch information
panchoh committed Mar 12, 2021
2 parents 175f174 + 1e63f3c commit 7807ccd
Show file tree
Hide file tree
Showing 37 changed files with 2,160 additions and 272 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test_and_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
docker build . -t bbvalabsci/kapow-spec-test-suite:latest
- name: Spec test
run: |
docker run --mount type=bind,source=$(pwd)/build/kapow,target=/usr/local/bin/kapow bbvalabsci/kapow-spec-test-suite:latest behave --tags=~@skip
docker run --mount type=bind,source=$(pwd)/build/kapow,target=/usr/bin/kapow bbvalabsci/kapow-spec-test-suite:latest "behave --tags=~@skip"
doc-test:
runs-on: ubuntu-20.04
steps:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ build

docs/build
docs/Pipfile.lock

node_modules

*.swp
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ coverage: test race
install: build
CGO_ENABLED=0 $(GOINSTALL) ./...

acceptance: install
make -C ./spec/test
acceptance: build
cd ./spec/test && PATH=$(PWD)/build:$$PATH nix-shell --command make

deps:
@echo "deps here"
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ You can find the complete documentation and examples [here](https://kapow.readth

## Security

Please consider the following security caveats **before** using *Kapow!*

- [Issue #119](https://github.com/BBVA/kapow/issues/119)
- [Security Concerns](https://kapow.readthedocs.io/en/stable/the_project/security.html#security-concerns)
Please consider the following
[Security Concerns](https://kapow.readthedocs.io/en/stable/the_project/security.html#security-concerns)
**before** using *Kapow!*

If you are not 100% sure about what you are doing we recommend not using *Kapow!*

Expand Down
14 changes: 9 additions & 5 deletions docs/source/concepts/interfaces.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ By default it binds to address ``0.0.0.0`` and port ``8080``, but that can be
changed via the ``--bind`` flag.


.. _http-control-interface:
.. _https-control-interface:

HTTP Control Interface
----------------------
HTTPS Control Interface
-----------------------

The `HTTP Control Interface` is used by the command ``kapow route`` to
The `HTTPS Control Interface` is used by the command ``kapow route`` to
administer the list of system routes.

This interface uses mTLS by default (double-pinned autogenerated certs).

By default it binds to address ``127.0.0.1`` and port ``8081``, but that can be
changed via the ``--control-bind`` flag.
changed via the ``--control-bind`` flag. If this is the case, consider
also ``--control-reachable-addr`` which will configure the autogenerated
certificate to match that address.


.. _http-data-interface:
Expand Down
4 changes: 2 additions & 2 deletions docs/source/concepts/request_life_cycle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ The spawned entrypoint is run with the following variables added to its
environment:

- :envvar:`KAPOW_HANDLER_ID`: Containing the `HANDLER_ID`
- :envvar:`KAPOW_DATAAPI_URL`: With the URL of the :ref:`http-data-interface`
- :envvar:`KAPOW_CONTROLAPI_URL`: With the URL of the :ref:`http-control-interface`
- :envvar:`KAPOW_DATA_URL`: With the URL of the :ref:`http-data-interface`
- :envvar:`KAPOW_CONTROL_URL`: With the URL of the :ref:`https-control-interface`


3. ``kapow set /response/body banana``
Expand Down
2 changes: 1 addition & 1 deletion docs/source/examples/managing_routes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Or, if you want human-readable output, you can use :program:`jq`:
.. note::

*Kapow!* has a :ref:`http-control-interface`, bound by default to
*Kapow!* has a :ref:`https-control-interface`, bound by default to
``localhost:8081``.


Expand Down
99 changes: 99 additions & 0 deletions internal/certs/certs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package certs

import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"net"
"time"

"github.com/BBVA/kapow/internal/logger"
)

type Cert struct {
X509Cert *x509.Certificate
PrivKey crypto.PrivateKey
SignedCert []byte
}

func (c Cert) SignedCertPEMBytes() []byte {

PEM := new(bytes.Buffer)
err := pem.Encode(PEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: c.SignedCert,
})
if err != nil {
logger.L.Fatal(err)
}

return PEM.Bytes()
}

func (c Cert) PrivateKeyPEMBytes() []byte {
PEM := new(bytes.Buffer)
err := pem.Encode(PEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(c.PrivKey.(*rsa.PrivateKey)),
})
if err != nil {
logger.L.Fatal(err)
}

return PEM.Bytes()
}

func GenCert(name, altName string, isServer bool) Cert {

usage := x509.ExtKeyUsageClientAuth
if isServer {
usage = x509.ExtKeyUsageServerAuth
}

var dnsNames []string
var ipAddresses []net.IP
if altName != "" {
if ipAddr := net.ParseIP(altName); ipAddr != nil {
ipAddresses = []net.IP{ipAddr}
} else {
dnsNames = []string{altName}
}
}

cert := &x509.Certificate{
SerialNumber: big.NewInt(1),
DNSNames: dnsNames,
IPAddresses: ipAddresses,
Subject: pkix.Name{
CommonName: name,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
IsCA: false,
BasicConstraintsValid: true,
ExtKeyUsage: []x509.ExtKeyUsage{
usage,
},
}

certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
logger.L.Fatal(err)
}

certBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &certPrivKey.PublicKey, certPrivKey)
if err != nil {
logger.L.Fatal(err)
}

return Cert{
X509Cert: cert,
PrivKey: certPrivKey,
SignedCert: certBytes,
}
}
29 changes: 29 additions & 0 deletions internal/client/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2019 Banco Bilbao Vizcaya Argentaria, S.A.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package client

import (
"os"
"testing"

"github.com/BBVA/kapow/internal/http"
)

func TestMain(m *testing.M) {
http.ControlClientGenerator = nil
os.Exit(m.Run())
}
2 changes: 1 addition & 1 deletion internal/client/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ import (
// GetData will perform the request and write the results on the provided writer
func GetData(host, id, path string, w io.Writer) error {
url := host + "/handlers/" + id + path
return http.Get(url, "", nil, w)
return http.Get(url, nil, w, nil)
}
2 changes: 1 addition & 1 deletion internal/client/route_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ func AddRoute(host, path, method, entrypoint, command string, w io.Writer) error
payload["entrypoint"] = entrypoint
}
body, _ := json.Marshal(payload)
return http.Post(url, "application/json", bytes.NewReader(body), w)
return http.Post(url, bytes.NewReader(body), w, http.ControlClientGenerator, http.AsJSON)
}
2 changes: 1 addition & 1 deletion internal/client/route_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ import (
// ListRoutes queries the kapow! instance for the routes that are registered
func ListRoutes(host string, w io.Writer) error {
url := host + "/routes"
return http.Get(url, "", nil, w)
return http.Get(url, nil, w, http.ControlClientGenerator)
}
2 changes: 1 addition & 1 deletion internal/client/route_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ import (
// RemoveRoute removes a registered route in Kapow! server
func RemoveRoute(host, id string) error {
url := host + "/routes/" + id
return http.Delete(url, "", nil, nil)
return http.Delete(url, nil, nil, http.ControlClientGenerator)
}
2 changes: 1 addition & 1 deletion internal/client/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ import (

func SetData(host, handlerID, path string, r io.Reader) error {
url := host + "/handlers/" + handlerID + path
return http.Put(url, "", r, nil)
return http.Put(url, r, nil, nil)
}
6 changes: 3 additions & 3 deletions internal/cmd/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func init() {
}
},
}
routeListCmd.Flags().String("control-url", getEnv("KAPOW_CONTROL_URL", "http://localhost:8081"), "Kapow! control interface URL")
routeListCmd.Flags().String("control-url", getEnv("KAPOW_CONTROL_URL", "https://localhost:8081"), "Kapow! control interface URL")

// TODO: Manage args for url_pattern and command_file (2 exact args)
var routeAddCmd = &cobra.Command{
Expand Down Expand Up @@ -78,7 +78,7 @@ func init() {
},
}
// TODO: Add default values for flags and remove path flag
routeAddCmd.Flags().String("control-url", getEnv("KAPOW_CONTROL_URL", "http://localhost:8081"), "Kapow! control interface URL")
routeAddCmd.Flags().String("control-url", getEnv("KAPOW_CONTROL_URL", "https://localhost:8081"), "Kapow! control interface URL")
routeAddCmd.Flags().StringP("method", "X", "GET", "HTTP method to accept")
routeAddCmd.Flags().StringP("entrypoint", "e", "", "Command to execute")
routeAddCmd.Flags().StringP("command", "c", "", "Command to pass to the shell")
Expand All @@ -95,7 +95,7 @@ func init() {
}
},
}
routeRemoveCmd.Flags().String("control-url", getEnv("KAPOW_CONTROL_URL", "http://localhost:8081"), "Kapow! control interface URL")
routeRemoveCmd.Flags().String("control-url", getEnv("KAPOW_CONTROL_URL", "https://localhost:8081"), "Kapow! control interface URL")

RouteCmd.AddCommand(routeListCmd)
RouteCmd.AddCommand(routeAddCmd)
Expand Down
Loading

0 comments on commit 7807ccd

Please sign in to comment.