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

Loads the mustache template found in :filename. If :template is provided it will be used instead of reading the file's content.

Iodine::Mustache.new(filename, template = nil)

When template data is provided, filename (if any) will only be used for partial template path resolution and the template data will be used for the template's content. This allows, for example, for front matter to be extracted before parsing the template.

Once a template was loaded, it could be rendered using render.

Accepts named arguments as well:

Iodine::Mustache.new(filename: "foo.mustache", template: "{{ bar }}")


228
229
230
231
232
233
# File 'ext/iodine/iodine_tls.c', line 228

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.



198
199
200
201
202
203
204
205
206
207
208
# File 'ext/iodine/iodine_tls.c', line 198

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.



166
167
168
169
170
171
172
173
# File 'ext/iodine/iodine_tls.c', line 166

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.



109
110
111
112
113
114
115
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
# File 'ext/iodine/iodine_tls.c', line 109

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;
}