tls_client.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package runtime
  2. import (
  3. "crypto/sha256"
  4. "crypto/subtle"
  5. "crypto/tls"
  6. "encoding/base64"
  7. "encoding/hex"
  8. "net/http"
  9. "strings"
  10. "time"
  11. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  12. "github.com/mhsanaei/3x-ui/v3/internal/util/common"
  13. "github.com/mhsanaei/3x-ui/v3/internal/util/netproxy"
  14. "github.com/mhsanaei/3x-ui/v3/internal/util/netsafe"
  15. )
  16. // defaultNodeHTTPClient reaches nodes trusting the system CA store ("verify"
  17. // mode or plain http); shared so connections pool across nodes.
  18. var defaultNodeHTTPClient = &http.Client{
  19. Transport: &http.Transport{
  20. MaxIdleConns: 64,
  21. MaxIdleConnsPerHost: 4,
  22. IdleConnTimeout: 60 * time.Second,
  23. DialContext: netsafe.SSRFGuardedDialContext,
  24. },
  25. }
  26. func HTTPClientForNode(n *model.Node, proxyURL string) (*http.Client, error) {
  27. mode := n.TlsVerifyMode
  28. if mode == "" {
  29. mode = "verify"
  30. }
  31. if proxyURL != "" {
  32. client, err := netproxy.NewHTTPClient(proxyURL, remoteHTTPTimeout)
  33. if err != nil {
  34. return nil, err
  35. }
  36. if mode == "verify" || n.Scheme == "http" {
  37. return client, nil
  38. }
  39. transport, ok := client.Transport.(*http.Transport)
  40. if !ok {
  41. return client, nil
  42. }
  43. tlsCfg, err := tlsConfigForNode(n)
  44. if err != nil {
  45. return nil, err
  46. }
  47. transport.TLSClientConfig = tlsCfg
  48. return client, nil
  49. }
  50. if mode == "verify" || n.Scheme == "http" {
  51. return defaultNodeHTTPClient, nil
  52. }
  53. tlsCfg, err := tlsConfigForNode(n)
  54. if err != nil {
  55. return nil, err
  56. }
  57. return &http.Client{
  58. Transport: &http.Transport{
  59. MaxIdleConns: 64,
  60. MaxIdleConnsPerHost: 4,
  61. IdleConnTimeout: 60 * time.Second,
  62. DialContext: netsafe.SSRFGuardedDialContext,
  63. TLSClientConfig: tlsCfg,
  64. },
  65. }, nil
  66. }
  67. func tlsConfigForNode(n *model.Node) (*tls.Config, error) {
  68. tlsCfg := &tls.Config{InsecureSkipVerify: true} // lgtm[go/disabled-certificate-check]
  69. if n.TlsVerifyMode == "pin" {
  70. want, err := DecodeCertPin(n.PinnedCertSha256)
  71. if err != nil {
  72. return nil, err
  73. }
  74. tlsCfg.VerifyConnection = func(cs tls.ConnectionState) error {
  75. if len(cs.PeerCertificates) == 0 {
  76. return common.NewError("node presented no certificate")
  77. }
  78. sum := sha256.Sum256(cs.PeerCertificates[0].Raw)
  79. if subtle.ConstantTimeCompare(sum[:], want) != 1 {
  80. return common.NewError("node certificate does not match pinned SHA-256")
  81. }
  82. return nil
  83. }
  84. }
  85. return tlsCfg, nil
  86. }
  87. // DecodeCertPin decodes a SHA-256 cert pin given as base64 (Xray's
  88. // pinnedPeerCertSha256 form) or hex with optional colons into 32 raw bytes.
  89. func DecodeCertPin(s string) ([]byte, error) {
  90. s = strings.TrimSpace(s)
  91. if s == "" {
  92. return nil, common.NewError("certificate pin is empty")
  93. }
  94. if b, err := hex.DecodeString(strings.ReplaceAll(s, ":", "")); err == nil && len(b) == sha256.Size {
  95. return b, nil
  96. }
  97. for _, enc := range []*base64.Encoding{base64.StdEncoding, base64.RawStdEncoding, base64.URLEncoding, base64.RawURLEncoding} {
  98. if b, err := enc.DecodeString(s); err == nil && len(b) == sha256.Size {
  99. return b, nil
  100. }
  101. }
  102. return nil, common.NewError("certificate pin must be a SHA-256 hash (base64 or hex)")
  103. }