1 // pubsubplus-go-client 2 // 3 // Copyright 2021-2024 Solace Corporation. All rights reserved. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package config 18 19 import ( 20 "time" 21 ) 22 23 // AuthenticationStrategy represents an authentication strategy. 24 type AuthenticationStrategy struct { 25 config ServicePropertyMap 26 } 27 28 func (strategy AuthenticationStrategy) String() string { 29 return strategy.config.String() 30 } 31 32 // ToProperties gets the configuration of the authentication strategy in the form of a ServicePropertyMap. 33 func (strategy AuthenticationStrategy) ToProperties() ServicePropertyMap { 34 // return empty map if config is nil 35 if strategy.config == nil { 36 return make(ServicePropertyMap) 37 } 38 // duplicate config 39 return strategy.config.GetConfiguration() 40 } 41 42 // BasicUserNamePasswordAuthentication gets a basic authentication strategy with the provided credentials. 43 func BasicUserNamePasswordAuthentication(username, password string) AuthenticationStrategy { 44 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 45 authenticationStrategy.config[AuthenticationPropertySchemeBasicUserName] = username 46 authenticationStrategy.config[AuthenticationPropertySchemeBasicPassword] = password 47 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeBasic 48 return authenticationStrategy 49 } 50 51 // ClientCertificateAuthentication gets a client certificate-based authentication strategy using 52 // the provided certificate file, key file, and key password if required. If no keyFile or keyPassword is 53 // required, use an empty string for the keyFile and keyPassword arguments. 54 func ClientCertificateAuthentication(certificateFile, keyFile, keyPassword string) AuthenticationStrategy { 55 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 56 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeClientCertificate 57 authenticationStrategy.config[AuthenticationPropertySchemeSSLClientCertFile] = certificateFile 58 if keyFile != "" { 59 authenticationStrategy.config[AuthenticationPropertySchemeSSLClientPrivateKeyFile] = keyFile 60 } 61 if keyPassword != "" { 62 authenticationStrategy.config[AuthenticationPropertySchemeClientCertPrivateKeyFilePassword] = keyPassword 63 } 64 return authenticationStrategy 65 } 66 67 // OAuth2Authentication creates an OAuth2-based authentication strategy using the specified tokens. 68 // At least one of accessToken or OIDC idToken must be provided. Optionally, issuerIdentifier 69 // can be provided. If any of the parameters is not required, an empty string can be passed. 70 func OAuth2Authentication(accessToken, oidcIDToken, issuerIdentifier string) AuthenticationStrategy { 71 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 72 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeOAuth2 73 if len(accessToken) > 0 { 74 authenticationStrategy.config[AuthenticationPropertySchemeOAuth2AccessToken] = accessToken 75 } 76 // Only set the id token or issuer identifier if they are nonempty. 77 if len(oidcIDToken) > 0 { 78 authenticationStrategy.config[AuthenticationPropertySchemeOAuth2OIDCIDToken] = oidcIDToken 79 } 80 if len(issuerIdentifier) > 0 { 81 authenticationStrategy.config[AuthenticationPropertySchemeOAuth2IssuerIdentifier] = issuerIdentifier 82 } 83 return authenticationStrategy 84 } 85 86 // KerberosAuthentication creates a Kerberos-based authentication strategy. Optionally, 87 // a Kerberos service name can be provided. If the service name is not required, an empty 88 // string can be passed to the serviceName argument. 89 // 90 // To implement Kerberos authentication for clients connecting to a broker, the following 91 // configuration is required the broker: 92 // - A Kerberos Keytab must be loaded on the broker. See "Event Broker File Management" for more information 93 // on the Solace documentation website. 94 // - Kerberos authentication must be configured and enabled for any Message VPNs that 95 // Kerberos-authenticated clients will connect to. 96 // - Optional: On an appliance, a Kerberos Service Principal Name (SPN) can be assigned to the IP address 97 // for the message backbone VRF Kerberos‑authenticated clients will use. 98 // 99 // Further reference can be found at 100 // 101 // https://docs.solace.com/Configuring-and-Managing/Configuring-Client-Authentication.htm#Config-Kerberos 102 func KerberosAuthentication(serviceName string) AuthenticationStrategy { 103 authenticationStrategy := AuthenticationStrategy{make(ServicePropertyMap)} 104 authenticationStrategy.config[AuthenticationPropertyScheme] = AuthenticationSchemeKerberos 105 if len(serviceName) > 0 { 106 authenticationStrategy.config[AuthenticationPropertySchemeKerberosInstanceName] = serviceName 107 } 108 return authenticationStrategy 109 } 110 111 /* Retry Strategy */ 112 113 // RetryStrategy represents the strategy to use when reconnecting. 114 type RetryStrategy struct { 115 retries int 116 retryInterval time.Duration 117 } 118 119 // GetRetries returns the number of retries configured with the retry strategy. 120 func (strategy RetryStrategy) GetRetries() int { 121 return strategy.retries 122 } 123 124 // GetRetryInterval returns the interval between (re)connection retry attempts. 125 func (strategy RetryStrategy) GetRetryInterval() time.Duration { 126 return strategy.retryInterval 127 } 128 129 // RetryStrategyForeverRetry creates a retry strategy that runs forever 130 // with automatic retries and a retry interval of 3000 milliseconds. 131 func RetryStrategyForeverRetry() RetryStrategy { 132 return RetryStrategy{-1, 3000 * time.Millisecond} 133 } 134 135 // RetryStrategyForeverRetryWithInterval creates a retry strategy that runs forever 136 // with automatic retries and a retry interval of the specified duration. 137 func RetryStrategyForeverRetryWithInterval(retryInterval time.Duration) RetryStrategy { 138 return RetryStrategy{-1, retryInterval} 139 } 140 141 // RetryStrategyNeverRetry creates an instance of configuration for disabled automatic retries. 142 func RetryStrategyNeverRetry() RetryStrategy { 143 return RetryStrategy{0, 3000 * time.Millisecond} 144 } 145 146 // RetryStrategyParameterizedRetry creates an instance of configuration for free configurability. 147 // retries must be greater than or equal to zero. 148 func RetryStrategyParameterizedRetry(retries uint, retryInterval time.Duration) RetryStrategy { 149 return RetryStrategy{int(retries), retryInterval} 150 } 151 152 /* Transport Security Strategy */ 153 154 // TransportSecurityStrategy represents the strategy to use when connecting to a broker 155 // via a secure port. 156 type TransportSecurityStrategy struct { 157 config ServicePropertyMap 158 } 159 160 // TransportSecurityProtocol represents the various security protocols available to the API. 161 type TransportSecurityProtocol string 162 163 const ( 164 // TransportSecurityProtocolSSLv3 represents SSLv3. 165 TransportSecurityProtocolSSLv3 TransportSecurityProtocol = "SSLv3" 166 // TransportSecurityProtocolTLSv1 represents TLSv1. 167 TransportSecurityProtocolTLSv1 TransportSecurityProtocol = "TLSv1" 168 // TransportSecurityProtocolTLSv1_1 represents TLSv1.1. 169 TransportSecurityProtocolTLSv1_1 TransportSecurityProtocol = "TLSv1.1" 170 // TransportSecurityProtocolTLSv1_2 represents TLSv1.2. 171 TransportSecurityProtocolTLSv1_2 TransportSecurityProtocol = "TLSv1.2" 172 ) 173 174 // NewTransportSecurityStrategy creates a transport security strategy with default configuration. 175 // Properties can be overwritten by calling the various configuration functions on TransportSecurityStrategy. 176 func NewTransportSecurityStrategy() TransportSecurityStrategy { 177 tss := TransportSecurityStrategy{ 178 config: make(ServicePropertyMap), 179 } 180 return tss 181 } 182 183 // Downgradable configures TLS so that the session connection is downgraded to plain-text after client authentication. 184 // WARNING: Downgrading SSL to plain-text after client authentication exposes a client and the data being sent 185 // to higher security risks. 186 func (tss TransportSecurityStrategy) Downgradable() TransportSecurityStrategy { 187 tss.config[TransportLayerSecurityPropertyProtocolDowngradeTo] = DowngradeToPlaintext 188 return tss 189 } 190 191 // WithExcludedProtocols specifies the list of SSL or TLS protocols to not use. 192 func (tss TransportSecurityStrategy) WithExcludedProtocols(protocols ...TransportSecurityProtocol) TransportSecurityStrategy { 193 protocolsCS := "" 194 for _, protocol := range protocols { 195 if protocolsCS != "" { 196 protocolsCS += "," 197 } 198 protocolsCS += string(protocol) 199 } 200 tss.config[TransportLayerSecurityPropertyExcludedProtocols] = protocolsCS 201 return tss 202 } 203 204 // WithoutCertificateValidation configures TLS to not validate the server certificate 205 // configured on the remote broker. 206 // WARNING: Disabling certificate validation exposes clients and data being sent to higher security risks. 207 func (tss TransportSecurityStrategy) WithoutCertificateValidation() TransportSecurityStrategy { 208 tss.config[TransportLayerSecurityPropertyCertValidated] = false 209 return tss 210 } 211 212 // WithCertificateValidation configures TLS validation on certificates. By default, validation is performed. 213 // WARNING: Disabling certificate validation exposes clients and data being sent to higher security risks. 214 // 215 // ignoreExpiration: when set to true, expired certificates are accepted. 216 // 217 // validateServerName: When set to true, certificates without the matching host are not accepted. 218 // 219 // trustStoreFilePath: The location of the trust store files. If an empty string is passed, no file path will be set. 220 // 221 // trustedCommonNameList: A comma-separated list of acceptable common names for matching with server certificates. An empty string will match no names. 222 func (tss TransportSecurityStrategy) WithCertificateValidation( 223 ignoreExpiration bool, 224 validateServerName bool, 225 trustStoreFilePath string, 226 trustedCommonNameList string, 227 ) TransportSecurityStrategy { 228 tss.config[TransportLayerSecurityPropertyCertValidated] = true 229 tss.config[TransportLayerSecurityPropertyCertRejectExpired] = !ignoreExpiration 230 tss.config[TransportLayerSecurityPropertyCertValidateServername] = validateServerName 231 if trustStoreFilePath != "" { 232 tss.config[TransportLayerSecurityPropertyTrustStorePath] = trustStoreFilePath 233 } 234 if trustedCommonNameList != "" { 235 tss.config[TransportLayerSecurityPropertyTrustedCommonNameList] = trustedCommonNameList 236 } 237 return tss 238 } 239 240 // WithCipherSuites configures cipher suites to use. The cipher suites value is a comma-separated 241 // list of cipher suites and must be from the following table: 242 // 243 // +-----------------+-------------------------------+--------------------+ 244 // | 'AES256-SHA' | 'ECDHE-RSA-AES256-SHA' | 'AES256-GCM-SHA384'| 245 // +-----------------+-------------------------------+--------------------+ 246 // | 'AES256-SHA256' | 'ECDHE-RSA-AES256-GCM-SHA384' | 'AES128-SHA256' | 247 // +-----------------+-------------------------------+--------------------+ 248 // | 'DES-CBC3-SHA' | 'ECDHE-RSA-DES-CBC3-SHA' | | 249 // +-----------------+-------------------------------+--------------------+ 250 // | 'RC4-SHA' | 'ECDHE-RSA-AES256-SHA384' | 'AES128 | 251 // +-----------------+-------------------------------+--------------------+ 252 // | 'ECDHE-RSA-AES128-SHA256' | 'AES128-GCM-SHA256'| 253 // +-----------------+-------------------------------+--------------------+ 254 // | 'RC4-MD5' | 'ECDHE-RSA-AES128-GCM-SHA256' | | 255 // +----------------------------------------+-----------------------------+ 256 // | 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'| 'ECDHE-RSA-AES128-SHA' | 257 // +----------------------------------------+-----------------------------+ 258 // | 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384'| | 259 // +----------------------------------------+-----------------------------+ 260 // | 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA' | | 261 // +----------------------------------------+-----------------------------+ 262 // | 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA' | | 263 // +----------------------------------------+-----------------------------+ 264 // | 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'| | 265 // +----------------------------------------+-----------------------------+ 266 // | 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA' | | 267 // +----------------------------------------+-----------------------------+ 268 // | 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256'| | 269 // +-----------------------------------+----------------------------------+ 270 // | 'TLS_RSA_WITH_AES_128_GCM_SHA256' | | 271 // +-----------------------------------+----------------------------------+ 272 // | 'TLS_RSA_WITH_AES_128_CBC_SHA256' |'TLS_RSA_WITH_AES_256_GCM_SHA384' | 273 // +-----------------------------------+----------------------------------+ 274 // | 'TLS_RSA_WITH_AES_256_CBC_SHA256' | 'TLS_RSA_WITH_AES_256_CBC_SHA' | 275 // +-----------------------------------+----------------------------------+ 276 // | 'SSL_RSA_WITH_3DES_EDE_CBC_SHA | 'TLS_RSA_WITH_AES_128_CBC_SHA' | 277 // +-----------------------------------+----------------------------------+ 278 // | 'SSL_RSA_WITH_RC4_128_SHA' | 'SSL_RSA_WITH_RC4_128_MD5' | 279 // +-----------------------------------+----------------------------------+ 280 func (tss TransportSecurityStrategy) WithCipherSuites(cipherSuiteList string) TransportSecurityStrategy { 281 tss.config[TransportLayerSecurityPropertyCipherSuites] = cipherSuiteList 282 return tss 283 } 284 285 // ToProperties returns the configuration in the form of a ServicePropertyMap. 286 func (tss TransportSecurityStrategy) ToProperties() ServicePropertyMap { 287 if tss.config == nil { 288 return make(ServicePropertyMap) 289 } 290 return tss.config.GetConfiguration() 291 } 292