diff --git a/doc/6-object-types.md b/doc/6-object-types.md
index 3b87ff8a1..049fc5e6a 100644
--- a/doc/6-object-types.md
+++ b/doc/6-object-types.md
@@ -51,6 +51,7 @@ Configuration Attributes:
accept\_config |**Optional.** Accept zone configuration. Defaults to `false`.
accept\_commands |**Optional.** Accept remote commands. Defaults to `false`.
cipher\_list |**Optional.** Cipher list that is allowed.
+ tls\_protocolmin |**Optional.** Minimum TLS protocol version. Must be one of `TLSv1`, `TLSv1.1` or `TLSv1.2`. Defaults to `TLSv1`.
## ApiUser
diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp
index 0315a1dbd..057fc1840 100644
--- a/lib/base/tlsutility.cpp
+++ b/lib/base/tlsutility.cpp
@@ -181,6 +181,28 @@ void SetCipherListToSSLContext(const boost::shared_ptr& context, const
}
}
+/**
+ * Set the minimum TLS protocol version to the specified SSL context.
+ *
+ * @param context The ssl context.
+ * @param tlsProtocolmin The minimum TLS protocol version.
+ */
+void SetTlsProtocolminToSSLContext(const boost::shared_ptr& context, const String& tlsProtocolmin)
+{
+ long flags = SSL_CTX_get_options(context.get());
+
+ flags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+
+ if (tlsProtocolmin == SSL_TXT_TLSV1_1)
+ flags |= SSL_OP_NO_TLSv1;
+ else if (tlsProtocolmin == SSL_TXT_TLSV1_2)
+ flags |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
+ else if (tlsProtocolmin != SSL_TXT_TLSV1)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid TLS protocol version specified."));
+
+ SSL_CTX_set_options(context.get(), flags);
+}
+
/**
* Loads a CRL and appends its certificates to the specified SSL context.
*
diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp
index 6a41d4818..aef59af44 100644
--- a/lib/base/tlsutility.hpp
+++ b/lib/base/tlsutility.hpp
@@ -41,6 +41,7 @@ void I2_BASE_API InitializeOpenSSL(void);
boost::shared_ptr I2_BASE_API MakeSSLContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String());
void I2_BASE_API AddCRLToSSLContext(const boost::shared_ptr& context, const String& crlPath);
void I2_BASE_API SetCipherListToSSLContext(const boost::shared_ptr& context, const String& cipherList);
+void I2_BASE_API SetTlsProtocolminToSSLContext(const boost::shared_ptr& context, const String& tlsProtocolmin);
String I2_BASE_API GetCertificateCN(const boost::shared_ptr& certificate);
boost::shared_ptr I2_BASE_API GetX509Certificate(const String& pemfile);
int I2_BASE_API MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), const String& serialFile = String(), bool ca = false);
diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp
index e1b002528..370c47a1a 100644
--- a/lib/remote/apilistener.cpp
+++ b/lib/remote/apilistener.cpp
@@ -104,6 +104,14 @@ void ApiListener::OnConfigLoaded(void)
+ GetCipherList() + "'.", GetDebugInfo()));
}
}
+
+ if (!GetTlsProtocolmin().IsEmpty()){
+ try {
+ SetTlsProtocolminToSSLContext(m_SSLContext, GetTlsProtocolmin());
+ } catch (const std::exception&) {
+ BOOST_THROW_EXCEPTION(ScriptError("Cannot set minimum TLS protocol version to SSL context with tls_protocolmin: '" + GetTlsProtocolmin() + "'.", GetDebugInfo()));
+ }
+ }
}
void ApiListener::OnAllConfigLoaded(void)
@@ -1171,3 +1179,14 @@ Endpoint::Ptr ApiListener::GetLocalEndpoint(void) const
{
return m_LocalEndpoint;
}
+
+void ApiListener::ValidateTlsProtocolmin(const String& value, const ValidationUtils& utils) override
+{
+ ObjectImpl::ValidateTlsProtocolmin(value, utils);
+
+ if (value != SSL_TXT_TLSV1 && value != SSL_TXT_TLSV1_1 &&
+ value != SSL_TXT_TLSV1_2) {
+ BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("tls_protocolmin"), "Invalid TLS version. "
+ "Must be one of '" SSL_TXT_TLSV1 "', '" SSL_TXT_TLSV1_1 "' or '" SSL_TXT_TLSV1_2 "'"));
+ }
+}
diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp
index 3f9a325a8..4c1771361 100644
--- a/lib/remote/apilistener.hpp
+++ b/lib/remote/apilistener.hpp
@@ -105,6 +105,8 @@ protected:
virtual void OnAllConfigLoaded(void) override;
virtual void Start(bool runtimeCreated) override;
+ virtual void ValidateTlsProtocolmin(const String& value, const ValidationUtils& utils) override;
+
private:
boost::shared_ptr m_SSLContext;
std::set m_Servers;
diff --git a/lib/remote/apilistener.ti b/lib/remote/apilistener.ti
index 91e4b0e51..fee11f2c8 100644
--- a/lib/remote/apilistener.ti
+++ b/lib/remote/apilistener.ti
@@ -35,6 +35,9 @@ class ApiListener : ConfigObject
[config] String cipher_list {
default {{{ return "ALL:!LOW:!WEAK:!MEDIUM:!EXP:!NULL"; }}}
};
+ [config] String tls_protocolmin {
+ default {{{ return "TLSv1"; }}}
+ };
[config] String bind_host;
[config] String bind_port {