Skip to content

Commit

Permalink
testtool: Added attested HTTPS demo
Browse files Browse the repository at this point in the history
Introduced mode serve to establish an attested HTTPS
server as well as mode request to perform example
attested HTTPS GET requests.

The addresses can be specified via the addr flag. Multiple
requests to the same server will be performed within the
same attested TLS session, while requests to different
servers trigger an attestation per server.

Signed-off-by: Simon Ott <[email protected]>
  • Loading branch information
smo4201 committed Feb 16, 2024
1 parent 4bae16c commit 91a23ff
Show file tree
Hide file tree
Showing 25 changed files with 1,079 additions and 301 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@ be used to establish attested TLS connections:
./testtool -mode dial -addr localhost:4443 -ca $CMC_ROOT/cmc-data/pki/ca.pem -mtls
```

### Establish Attested HTTPS Connection

The `testtool` can also perform user-specified attested HTTPS requests and act as an attested HTTPS demo server, respectively.

```sh
# Run two attested HTTPS servers
./testtool -config $CMC_ROOT/testtool-config.json -addr 0.0.0.0:8081 -mode serve
./testtool -config $CMC_ROOT/testtool-config.json -addr 0.0.0.0:8082 -mode serve

# Perform multiple user-specified attested HTTPS requests to both servers. Each connection is attested, while multiple requests to the same server use the established attested TLS connections
./testtool \
-config ../../cmc-data/testtool-lib-config.json \
-addr https://localhost:8081/post,https://localhost:8082/post \
-mode request \
-method POST \
-data "hello from attested HTTPS client" \
-header "Content-Type: text/plain"
```


**Note**: The *cmcd* TPM provisioning process includes the verification of the TPM's EK certificate
chain. In the example setup, this verification is turned off, as the database might not contain
the certificate chain for the TPM of the machine the *cmcd* is running on. Instead, simply a
Expand Down
212 changes: 212 additions & 0 deletions attestedhttp/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// Copyright (c) 2021 Fraunhofer AISEC
// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
//
// 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 attestedhttp

import (
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"time"

"github.com/sirupsen/logrus"

ar "github.com/Fraunhofer-AISEC/cmc/attestationreport"
atls "github.com/Fraunhofer-AISEC/cmc/attestedtls"
"github.com/Fraunhofer-AISEC/cmc/cmc"
)

var log = logrus.WithField("service", "ahttps")

// Wrapper for net/http Transport
type Transport struct {

// Wrapped http.Transport parameters
TLSClientConfig *tls.Config
TLSHandshakeTimeout time.Duration
DisableKeepAlives bool
DisableCompression bool
MaxIdleConns int
MaxIdleConnsPerHost int
MaxConnsPerHost int
IdleConnTimeout time.Duration
ResponseHeaderTimeout time.Duration
ExpectContinueTimeout time.Duration
MaxResponseHeaderBytes int64
WriteBufferSize int
ReadBufferSize int

// Some http.Transport parameters such as dial hooks are not exposed,
// as we enforce aTLS as the underlying transport protocol

// Additional aTLS parameters
Attest string
MutualTls bool
CmcAddr string
CmcApi atls.CmcApiSelect
CmcNetwork string
Cmc *cmc.Cmc
CmcPolicies []byte
Ca []byte
ReadTimeout time.Duration
}

// Wrapper for net/http Client
type Client struct {
Transport *Transport
CheckRedirect func(req *http.Request, via []*http.Request) error
Jar http.CookieJar
Timeout time.Duration

client *http.Client
}

// Wrapper for net/http client.Get()
func (c *Client) Get(url string) (resp *http.Response, err error) {

err = prepareClient(c)
if err != nil {
return nil, fmt.Errorf("failed to prepare client: %w", err)
}

log.Debugf("Performing aHTTPS GET to %v", url)

return c.client.Get(url)
}

// Wrapper for net/http client.Do()
func (c *Client) Do(req *http.Request) (*http.Response, error) {

err := prepareClient(c)
if err != nil {
return nil, fmt.Errorf("failed to prepare client: %w", err)
}

log.Debugf("Performing aHTTPS %v to %v", req.Method, req.URL)

return c.client.Do(req)
}

// Wrapper for net/http client.Post()
func (c *Client) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {

err = prepareClient(c)
if err != nil {
return nil, fmt.Errorf("failed to prepare client: %w", err)
}

log.Debugf("Performing aHTTPS POST with content type %v to %v", contentType, url)

return c.client.Post(url, contentType, body)
}

// Wrapper for net/http client.Head()
func (c *Client) Head(url string) (resp *http.Response, err error) {

err = prepareClient(c)
if err != nil {
return nil, fmt.Errorf("failed to prepare client: %w", err)
}

log.Debugf("Performing aHTTPS HEAD to %v", url)

return c.client.Head(url)
}

// Wrapper for net/http client.CloseIdleConnections()
func (c *Client) CloseIdleConnections() {

if c.client == nil {
log.Warn("Cannot close idle connections: client not initialized")
}

log.Debug("Cloding idle connections")

c.client.CloseIdleConnections()
}

// Wrapper for net/http client.PostForm()
func (c *Client) PostForm(url string, data url.Values) (resp *http.Response, err error) {
return c.client.PostForm(url, data)
}

// Prepare the client's underlying attested TLS connection
func prepareClient(c *Client) error {
var cmcConfig atls.CmcConfig
if c.client == nil {

// Store aTLS and CMC configuration
cmcConfig.Attest = atls.GetAttestMode(c.Transport.Attest)
cmcConfig.Ca = c.Transport.Ca
cmcConfig.Cmc = c.Transport.Cmc
cmcConfig.CmcAddr = c.Transport.CmcAddr
cmcConfig.CmcApi = atls.CmcApis[c.Transport.CmcApi]
cmcConfig.Mtls = c.Transport.MutualTls
cmcConfig.Network = c.Transport.Cmc.Network
cmcConfig.Policies = c.Transport.CmcPolicies

log.Tracef("Initializing new HTTP client")
// Create http.Transport from wrapper
transport := &http.Transport{
// User-specified HTTPS values
TLSHandshakeTimeout: c.Transport.TLSHandshakeTimeout, // ignored because of custom DialContext
DisableKeepAlives: c.Transport.DisableKeepAlives,
DisableCompression: c.Transport.DisableCompression,
MaxIdleConns: c.Transport.MaxIdleConns,
MaxIdleConnsPerHost: c.Transport.MaxIdleConnsPerHost,
MaxConnsPerHost: c.Transport.MaxConnsPerHost,
ResponseHeaderTimeout: c.Transport.ResponseHeaderTimeout,
ExpectContinueTimeout: c.Transport.ExpectContinueTimeout,
MaxResponseHeaderBytes: c.Transport.MaxResponseHeaderBytes,
WriteBufferSize: c.Transport.WriteBufferSize,
ReadBufferSize: c.Transport.ReadBufferSize,
TLSClientConfig: c.Transport.TLSClientConfig, // Ignored becuase of custom DialContext
IdleConnTimeout: c.Transport.IdleConnTimeout,
// Fixed custom TLS dial function to enforce aHTTPS
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
if c.Transport.TLSClientConfig == nil {
return nil, errors.New("failed to dial internal: no TLS config provided")
}

log.Debugf("Dialing TLS address: %v", addr)

verificationResult := new(ar.VerificationResult)

conn, err := atls.Dial("tcp", addr, c.Transport.TLSClientConfig,
atls.WithCmcConfig(&cmcConfig),
atls.WithResult(verificationResult))
if err != nil {
return nil, fmt.Errorf("failed to dial server: %w", err)
}
if c.Transport.ReadTimeout != 0 {
_ = conn.SetReadDeadline(time.Now().Add(c.Transport.ReadTimeout))
}
// TODO store verification result for retrieval

log.Debugf("aHTTPS connection established")

return conn, err
},
}
c.client = &http.Client{Transport: transport}
}

return nil
}
79 changes: 79 additions & 0 deletions attestedhttp/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2021 Fraunhofer AISEC
// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
//
// 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 attestedhttp

import (
"errors"
"fmt"
"net/http"

ar "github.com/Fraunhofer-AISEC/cmc/attestationreport"
atls "github.com/Fraunhofer-AISEC/cmc/attestedtls"
"github.com/Fraunhofer-AISEC/cmc/cmc"
)

// Wrapper for http.Server
type Server struct {
Server *http.Server

// Additional aTLS parameters
Attest string
MutualTls bool
CmcAddr string
CmcApi atls.CmcApiSelect
CmcNetwork string
Cmc *cmc.Cmc
CmcPolicies []byte
Ca []byte
}

func (s *Server) ListenAndServe() error {

if s.Server.TLSConfig == nil {
return errors.New("failed to listen: no TLS config provided")
}

verificationResult := new(ar.VerificationResult)

// Listen: TLS connection
ln, err := atls.Listen("tcp", s.Server.Addr, s.Server.TLSConfig,
atls.WithCmcAddr(s.CmcAddr),
atls.WithCmcCa(s.Ca),
atls.WithCmcPolicies(s.CmcPolicies),
atls.WithCmcApi(s.CmcApi),
atls.WithMtls(s.MutualTls),
atls.WithAttest(s.Attest),
atls.WithCmcNetwork(s.CmcNetwork),
atls.WithResult(verificationResult),
atls.WithCmc(s.Cmc))
if err != nil {
log.Fatalf("Failed to listen for connections: %v", err)
}
defer ln.Close()

// TODO store verification result

log.Infof("Serving HTTPS under %v", s.Server.Addr)

err = s.Server.Serve(ln)
if err != nil {
return fmt.Errorf("failed to serve: %w", err)
}

log.Info("Finished serving HTTPS")

return nil
}
Loading

0 comments on commit 91a23ff

Please sign in to comment.