source dump of claude code
at main 179 lines 4.7 kB view raw
1import type * as https from 'https' 2import { Agent as HttpsAgent } from 'https' 3import memoize from 'lodash-es/memoize.js' 4import type * as tls from 'tls' 5import type * as undici from 'undici' 6import { getCACertificates } from './caCerts.js' 7import { logForDebugging } from './debug.js' 8import { getFsImplementation } from './fsOperations.js' 9 10export type MTLSConfig = { 11 cert?: string 12 key?: string 13 passphrase?: string 14} 15 16export type TLSConfig = MTLSConfig & { 17 ca?: string | string[] | Buffer 18} 19 20/** 21 * Get mTLS configuration from environment variables 22 */ 23export const getMTLSConfig = memoize((): MTLSConfig | undefined => { 24 const config: MTLSConfig = {} 25 26 // Note: NODE_EXTRA_CA_CERTS is automatically handled by Node.js at runtime 27 // We don't need to manually load it - Node.js appends it to the built-in CAs automatically 28 29 // Client certificate 30 if (process.env.CLAUDE_CODE_CLIENT_CERT) { 31 try { 32 config.cert = getFsImplementation().readFileSync( 33 process.env.CLAUDE_CODE_CLIENT_CERT, 34 { encoding: 'utf8' }, 35 ) 36 logForDebugging( 37 'mTLS: Loaded client certificate from CLAUDE_CODE_CLIENT_CERT', 38 ) 39 } catch (error) { 40 logForDebugging(`mTLS: Failed to load client certificate: ${error}`, { 41 level: 'error', 42 }) 43 } 44 } 45 46 // Client key 47 if (process.env.CLAUDE_CODE_CLIENT_KEY) { 48 try { 49 config.key = getFsImplementation().readFileSync( 50 process.env.CLAUDE_CODE_CLIENT_KEY, 51 { encoding: 'utf8' }, 52 ) 53 logForDebugging('mTLS: Loaded client key from CLAUDE_CODE_CLIENT_KEY') 54 } catch (error) { 55 logForDebugging(`mTLS: Failed to load client key: ${error}`, { 56 level: 'error', 57 }) 58 } 59 } 60 61 // Key passphrase 62 if (process.env.CLAUDE_CODE_CLIENT_KEY_PASSPHRASE) { 63 config.passphrase = process.env.CLAUDE_CODE_CLIENT_KEY_PASSPHRASE 64 logForDebugging('mTLS: Using client key passphrase') 65 } 66 67 // Only return config if at least one option is set 68 if (Object.keys(config).length === 0) { 69 return undefined 70 } 71 72 return config 73}) 74 75/** 76 * Create an HTTPS agent with mTLS configuration 77 */ 78export const getMTLSAgent = memoize((): HttpsAgent | undefined => { 79 const mtlsConfig = getMTLSConfig() 80 const caCerts = getCACertificates() 81 82 if (!mtlsConfig && !caCerts) { 83 return undefined 84 } 85 86 const agentOptions: https.AgentOptions = { 87 ...mtlsConfig, 88 ...(caCerts && { ca: caCerts }), 89 // Enable keep-alive for better performance 90 keepAlive: true, 91 } 92 93 logForDebugging('mTLS: Creating HTTPS agent with custom certificates') 94 return new HttpsAgent(agentOptions) 95}) 96 97/** 98 * Get TLS options for WebSocket connections 99 */ 100export function getWebSocketTLSOptions(): tls.ConnectionOptions | undefined { 101 const mtlsConfig = getMTLSConfig() 102 const caCerts = getCACertificates() 103 104 if (!mtlsConfig && !caCerts) { 105 return undefined 106 } 107 108 return { 109 ...mtlsConfig, 110 ...(caCerts && { ca: caCerts }), 111 } 112} 113 114/** 115 * Get fetch options with TLS configuration (mTLS + CA certs) for undici 116 */ 117export function getTLSFetchOptions(): { 118 tls?: TLSConfig 119 dispatcher?: undici.Dispatcher 120} { 121 const mtlsConfig = getMTLSConfig() 122 const caCerts = getCACertificates() 123 124 if (!mtlsConfig && !caCerts) { 125 return {} 126 } 127 128 const tlsConfig: TLSConfig = { 129 ...mtlsConfig, 130 ...(caCerts && { ca: caCerts }), 131 } 132 133 if (typeof Bun !== 'undefined') { 134 return { tls: tlsConfig } 135 } 136 logForDebugging('TLS: Created undici agent with custom certificates') 137 // Create a custom undici Agent with TLS options. Lazy-required so that 138 // the ~1.5MB undici package is only loaded when mTLS/CA certs are configured. 139 // eslint-disable-next-line @typescript-eslint/no-require-imports 140 const undiciMod = require('undici') as typeof undici 141 const agent = new undiciMod.Agent({ 142 connect: { 143 cert: tlsConfig.cert, 144 key: tlsConfig.key, 145 passphrase: tlsConfig.passphrase, 146 ...(tlsConfig.ca && { ca: tlsConfig.ca }), 147 }, 148 pipelining: 1, 149 }) 150 151 return { dispatcher: agent } 152} 153 154/** 155 * Clear the mTLS configuration cache. 156 */ 157export function clearMTLSCache(): void { 158 getMTLSConfig.cache.clear?.() 159 getMTLSAgent.cache.clear?.() 160 logForDebugging('Cleared mTLS configuration cache') 161} 162 163/** 164 * Configure global Node.js TLS settings 165 */ 166export function configureGlobalMTLS(): void { 167 const mtlsConfig = getMTLSConfig() 168 169 if (!mtlsConfig) { 170 return 171 } 172 173 // NODE_EXTRA_CA_CERTS is automatically handled by Node.js at runtime 174 if (process.env.NODE_EXTRA_CA_CERTS) { 175 logForDebugging( 176 'NODE_EXTRA_CA_CERTS detected - Node.js will automatically append to built-in CAs', 177 ) 178 } 179}