Class: Iodine::TLS

Inherits:
Data
  • Object
show all
Defined in:
lib/iodine/tls.rb,
ext/iodine/iodine_tls.c

Overview

Iodine's TLS instances hold SSL/TLS settings for secure connections.

This allows both secure client connections and secure server connections to be established.

tls = Iodine::TLS.new "localhost" # self-signed certificate
Iodine.listen service: "http", handler: APP, tls: tls

Iodine abstracts away the underlying SSL/TLS library to minimize the risk of misuse and insecure settings.

Calling TLS methods when no SSL/TLS library is available should result in iodine crashing. This is expected behavior.

At the moment, only OpenSSL is supported. BearSSL support is planned.

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Object

Creates a new Iodine::TLS object and calles the #use_certificate method with the supplied arguments.



221
222
223
224
225
226
# File 'ext/iodine/iodine_tls.c', line 221

static VALUE iodine_tls_new(int argc, VALUE *argv, VALUE self) {
  if (argc) {
    iodine_tls_use_certificate(argc, argv, self);
  }
  return self;
}

Instance Method Details

#on_protocol(protocol_name) ⇒ Object

Adds an ALPN protocol callback for the named protocol, the required block must return the handler for that protocol.

The first protocol added will be the default protocol in cases where ALPN failed.

i.e.:

 tls.on_protocol("http/1.1") { HTTPConnection.new }

When implementing TLS clients, this identifies the protocol(s) that should be requested by the client.

When implementing TLS servers, this identifies the protocol(s) offered by the server.

More than a single protocol can be set, but iodine doesn't offer, at this moment, a way to handle these changes or to detect which protocol was selected except by assigning a different callback per protocol.

This is implemented using the ALPN extension to TLS.



205
206
207
208
209
210
211
212
213
214
215
# File 'ext/iodine/iodine_tls.c', line 205

static VALUE iodine_tls_alpn(VALUE self, VALUE protocol_name) {
  Check_Type(protocol_name, T_STRING);
  rb_need_block();
  fio_tls_s *t = iodine_tls2c(self);
  char *prname =
      (protocol_name == Qnil ? NULL : IODINE_RSTRINFO(protocol_name).data);
  VALUE block = IodineStore.add(rb_block_proc());
  fio_tls_alpn_add(t, prname, iodine_tls_alpn_cb, (void *)block,
                   (void (*)(void *))IodineStore.remove);
  return self;
}

#trust(certificate) ⇒ Object

Adds a certificate PEM file to the list of trusted certificates and enforces peer verification.

This is extremely important when using Iodine::TLS for client connections, since adding the target server's

It is enough to add the Certificate Authority's (CA) certificate, there's no need to add each client or server certificate.

When #trust is used on a server TLS, only trusted clients will be allowed to connect.

Since TLS setup is crucial for security, a missing file will result in Iodine crashing with an error message. This is expected behavior.



173
174
175
176
177
178
179
180
# File 'ext/iodine/iodine_tls.c', line 173

static VALUE iodine_tls_trust(VALUE self, VALUE certificate) {
  Check_Type(certificate, T_STRING);
  fio_tls_s *t = iodine_tls2c(self);
  char *pubcert =
      (certificate == Qnil ? NULL : IODINE_RSTRINFO(certificate).data);
  fio_tls_trust(t, pubcert);
  return self;
}

#use_certificate(*args) ⇒ Object

Assigns the TLS context a public sertificate, allowing remote parties to validate the connection's identity.

A self signed certificate is automatically created if the server_name argument is specified and either (or both) of the certificate or private_ket arguments are missing.

Some implementations allow servers to have more than a single certificate, which will be selected using the SNI extension. I believe the existing OpenSSL implementation supports this option (untested).

 Iodine::TLS#use_certificate(server_name,
                             certificate = nil,
                             private_key = nil,
                             password = nil)

Certificates and keys should be String objects leading to a PEM file.

This method also accepts named arguments. i.e.:

 tls = Iodine::TLS.new
 tls.use_certificate server_name: "example.com"
 tls.use_certificate certificate: "my_cert.pem", private_key: "my_key.pem"

Since TLS setup is crucial for security, a missing file will result in Iodine crashing with an error message. This is expected behavior.



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'ext/iodine/iodine_tls.c', line 116

static VALUE iodine_tls_use_certificate(int argc, VALUE *argv, VALUE self) {
  VALUE server_name = Qnil, certificate = Qnil, private_key = Qnil,
        password = Qnil;
  if (argc == 1 && RB_TYPE_P(argv[0], T_HASH)) {
    /* named arguments */
    server_name = rb_hash_aref(argv[0], server_name_sym);
    certificate = rb_hash_aref(argv[0], certificate_sym);
    private_key = rb_hash_aref(argv[0], private_key_sym);
    password = rb_hash_aref(argv[0], password_sym);
  } else {
    /* regular arguments */
    switch (argc) {
    case 4: /* overflow */
      password = argv[3];
      Check_Type(password, T_STRING);
    case 3: /* overflow */
      private_key = argv[1];
      Check_Type(private_key, T_STRING);
    case 2: /* overflow */
      certificate = argv[2];
      Check_Type(certificate, T_STRING);
    case 1: /* overflow */
      server_name = argv[0];
      Check_Type(server_name, T_STRING);
      break;
    default:
      rb_raise(rb_eArgError, "expecting 1..4 arguments or named arguments.");
      return self;
    }
  }

  fio_tls_s *t = iodine_tls2c(self);
  char *srvname = (server_name == Qnil ? NULL : StringValueCStr(server_name));
  char *pubcert = (certificate == Qnil ? NULL : StringValueCStr(certificate));
  char *prvkey = (private_key == Qnil ? NULL : StringValueCStr(private_key));
  char *pass = (password == Qnil ? NULL : StringValueCStr(password));

  fio_tls_cert_add(t, srvname, pubcert, prvkey, pass);
  return self;
}