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 }