github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/signers/aaasvc_http.go (about)

     1  // Copyright (c) 2021, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package signers
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"crypto/tls"
    11  	"encoding/hex"
    12  	"encoding/json"
    13  	"fmt"
    14  	"io"
    15  	"net/http"
    16  
    17  	"github.com/choria-io/go-choria/inter"
    18  	iu "github.com/choria-io/go-choria/internal/util"
    19  )
    20  
    21  // NewAAAServiceHTTPSigner creates an AAA Signer that uses HTTP requests to the AAA Service
    22  func NewAAAServiceHTTPSigner() *aaaServiceHTTP {
    23  	return &aaaServiceHTTP{}
    24  }
    25  
    26  type aaaServiceHTTP struct{}
    27  
    28  type httpSigningRequest struct {
    29  	Token     string `json:"token"`
    30  	Request   []byte `json:"request"`
    31  	Signature string `json:"signature"`
    32  }
    33  
    34  type httpSigningReply struct {
    35  	Signed []byte `json:"secure_request"`
    36  	Error  string `json:"error"`
    37  }
    38  
    39  func (s *aaaServiceHTTP) Kind() string { return "AAA Service HTTP" }
    40  
    41  func (s *aaaServiceHTTP) Sign(_ context.Context, request []byte, cfg inter.RequestSignerConfig) ([]byte, error) {
    42  	signer, err := cfg.RemoteSignerURL()
    43  	if err != nil {
    44  		return nil, fmt.Errorf("remote signing URL not configured")
    45  	}
    46  
    47  	token, err := cfg.RemoteSignerToken()
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	sf, err := cfg.RemoteSignerSeedFile()
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	sigb, err := iu.Ed25519SignWithSeedFile(sf, request)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	req := &httpSigningRequest{
    63  		Request:   request,
    64  		Token:     string(token),
    65  		Signature: hex.EncodeToString(sigb),
    66  	}
    67  
    68  	client := &http.Client{}
    69  	if signer.Scheme == "https" {
    70  		client.Transport = &http.Transport{TLSClientConfig: &tls.Config{
    71  			MinVersion: tls.VersionTLS12,
    72  			// While this might appear alarming it's expected that the clients
    73  			// in this situation will not have any Choria CA issued certificates
    74  			// and so wish to use a remote signer - the certificate management woes
    75  			// being one of the main reasons for centralized AAA.
    76  			//
    77  			// So there is no realistic way to verify these requests especially in the
    78  			// event that these signers run on private IPs and such as would be typical
    79  			// so while we do this big No No of disabling verify here it really is the
    80  			// only thing that make sense.
    81  			InsecureSkipVerify: true,
    82  		}}
    83  	}
    84  
    85  	jreq, err := json.Marshal(req)
    86  	if err != nil {
    87  		return nil, fmt.Errorf("could not encode request: %s", err)
    88  	}
    89  
    90  	resp, err := client.Post(signer.String(), "application/json", bytes.NewBuffer(jreq))
    91  	if err != nil {
    92  		return nil, fmt.Errorf("could not perform signing request: %s", err)
    93  	}
    94  	defer resp.Body.Close()
    95  
    96  	if resp.StatusCode != 200 {
    97  		return nil, fmt.Errorf("could not perform remote signing request: %s", resp.Status)
    98  	}
    99  
   100  	body, err := io.ReadAll(resp.Body)
   101  	if err != nil {
   102  		return nil, fmt.Errorf("could not read signing response: %s", err)
   103  	}
   104  
   105  	signerResp := &httpSigningReply{}
   106  	err = json.Unmarshal(body, signerResp)
   107  	if err != nil {
   108  		return nil, fmt.Errorf("could not parse signer response: %s", err)
   109  	}
   110  
   111  	if signerResp.Error != "" {
   112  		return nil, fmt.Errorf("error from remote signer: %s", signerResp.Error)
   113  	}
   114  
   115  	return signerResp.Signed, nil
   116  
   117  }