2

I wrote a small javascript to test digest authentication on monerod and monero-wallet-rpc.

When running on a http (no tls) website, authentication works, and I get my response.

Then trying the same javascript on a https website, I get the folowing log output from the wallet server.

D handle_accept
     D New server for RPC connections, SSL autodetection
     D set m_connection_type = RPC 
     D Spawned connection #4 to 0.0.0.0 currently we have sockets count:2
     D test, connection constructor set m_connection_type=1
     T [sock 0x7f9bdc044680] new connection from client.ip.address:62186 INC to server.ip.address:38083, total sockets objects 2
     T New connection from host client.ip.address: 0
     T Setting 00:00:10 expiry
     D  connection type RPC server.ip.address:38083 <--> client.ip.address:62186 (via client.ip.address:62186)
     T Setting 00:00:10.102921 expiry
     D SSL detection buffer, 517 bytes: 22 3 1 2 0 1 0 1 252
     D That looks like SSL
     D handle_accept
     D New server for RPC connections, SSL autodetection
     D set m_connection_type = RPC 
     D Spawned connection #5 to 0.0.0.0 currently we have sockets count:3
     D test, connection constructor set m_connection_type=1
     T [sock 0x7f9bdc02dce0] new connection from client.ip.address:61715 INC to server.ip.address:38083, total sockets objects 3
     T New connection from host client.ip.address: 1
     T Setting 00:00:10 expiry
     D  connection type RPC server.ip.address:38083 <--> client.ip.address:61715 (via client.ip.address:61715)
     T Setting 00:00:10.072703 expiry
     D SSL detection buffer, 517 bytes: 22 3 1 2 0 1 0 1 252
     D That looks like SSL
     E SSL handshake failed, connection dropped: short read
     E SSL handshake failed
     T Closed connection from host client.ip.address: 2
     D SSL handshake success
     T [sock 9] Socket destroyed
     D Destructing connection #4 to client.ip.address
     T [sock 8] Some not success at read: sslv3 alert bad certificate:336151570
     T [sock 8] Some problems at read: sslv3 alert bad certificate:336151570
     T Closed connection from host client.ip.address: 1
     T [sock 8] Socket destroyed
     D Destructing connection #3 to client.ip.address

And I get a generic error in Firefox.

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://server.ip.address:38083/json_rpc. (Reason: CORS request did not succeed).

What am I missing? Do I need to tell monero about my certificate?

EDIT: Added a successful session. No HTTPS and No certificates.

Website and the monero stuff is on the same host/ip-address.

 T Throttle <<< global-IN: packet of ~496b  (from 496 b) Speed AVG=   0[w=9.757]    0[w=9.757] /  Limit=16 KiB/sec  [496 0 0 0 0 0 0 0 0 0 ]
 T HTTP HEAD:
 T Host: host.example.com:38083
 T User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0
 T Accept: */*
 T Accept-Language: en,da;q=0.5
 T Accept-Encoding: gzip, deflate
 T Access-Control-Request-Method: POST
 T Access-Control-Request-Headers: content-type
 T Referer: http://host.example.com/test-05/
 T Origin: http://host.example.com
 T DNT: 1
 T Connection: keep-alive
 T Pragma: no-cache
 T Cache-Control: no-cache
 T 
 T HTTP_RESPONSE_HEAD: << 
 T HTTP/1.1 200 OK
 T Server: Epee-based
 T Content-Length: 0
 T Last-Modified: Thu, 19 Mar 2020 14:55:13 GMT
 T Accept-Ranges: bytes
 T Access-Control-Allow-Origin: http://host.example.com
 T Access-Control-Expose-Headers: www-authenticate
 T Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
 T Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS
 T 
 T Moving counter buffer by 1 second 0 < 300414 (last time 0)
 T Throttle throttle_speed_out: packet of ~373b  (from 373 b) Speed AVG=   0[w=1]    0[w=1] /  Limit=16 KiB/sec  [373 0 0 0 0 0 0 0 0 0 ]
 D do_send_chunk() NOW SENSD: packet=373 B
 T Setting 00:05:00 expiry
 T Setting 00:05:00 expiry
 T [client.ip.addr:60290 INC] [sock 8] Async send calledback 373
 D handle_accept
 D New server for RPC connections, SSL autodetection
 D set m_connection_type = RPC 
 D Spawned connection #8 to 0.0.0.0 currently we have sockets count:3
 D test, connection constructor set m_connection_type=1
 T [sock 0x7f147801ec40] new connection from client.ip.addr:34239 INC to server.ip.addr:38083, total sockets objects 3
 T New connection from host client.ip.addr: 1
 T Setting 00:00:10 expiry
 D  connection type RPC server.ip.addr:38083 <--> client.ip.addr:34239 (via client.ip.addr:34239)
 T Setting 00:00:09.738196 expiry
 D SSL detection buffer, 538 bytes: 80 79 83 84 32 47 106 115 111
 D That does not look like SSL
 T Moving counter buffer by 1 second 0 < 300415 (last time 0)
 T Throttle throttle_speed_in: packet of ~538b  (from 538 b) Speed AVG=   0[w=1]    0[w=1] /  Limit=16 KiB/sec  [538 0 0 0 0 0 0 0 0 0 ]
 T Moving counter buffer by 1 second 300413 < 300415 (last time 300414)
 T Moving counter buffer by 1 second 300414 < 300415 (last time 300415)
 T Throttle <<< global-IN: packet of ~538b  (from 538 b) Speed AVG=   0[w=9.159]    0[w=9.159] /  Limit=16 KiB/sec  [538 0 496 0 0 0 0 0 0 0 ]
 T HTTP HEAD:
 T Host: host.example.com:38083
 T User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0
 T Accept: */*
 T Accept-Language: en,da;q=0.5
 T Accept-Encoding: gzip, deflate
 T Content-Type: application/json; charset=utf-8
 T Content-Length: 61
 T Origin: http://host.example.com
 T DNT: 1
 T Connection: keep-alive
 T Referer: http://host.example.com/test-05/
 T Pragma: no-cache
 T Cache-Control: no-cache
 T 
 T HTTP_RESPONSE_HEAD: << 
 T HTTP/1.1 401 Unauthorized
 T Server: Epee-based
 T Content-Length: 98
 T Content-Type: text/html
 T Last-Modified: Thu, 19 Mar 2020 14:55:14 GMT
 T Accept-Ranges: bytes
 T Access-Control-Allow-Origin: http://host.example.com
 T Access-Control-Expose-Headers: www-authenticate
 T Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS
 T WWW-authenticate: Digest qop="auth",algorithm=MD5,realm="monero-rpc",nonce="TxiQqEIvHWWbjks8rvdMRA==",stale=false
 T WWW-authenticate: Digest qop="auth",algorithm=MD5-sess,realm="monero-rpc",nonce="TxiQqEIvHWWbjks8rvdMRA==",stale=false
 T 
 T Moving counter buffer by 1 second 0 < 300415 (last time 0)
 T Throttle throttle_speed_out: packet of ~665b  (from 665 b) Speed AVG=   0[w=1]    0[w=1] /  Limit=16 KiB/sec  [665 0 0 0 0 0 0 0 0 0 ]
 D do_send_chunk() NOW SENSD: packet=665 B
 T Setting 00:05:00 expiry
 T Setting 00:05:00 expiry
 T [client.ip.addr:34239 INC] [sock 9] Async send calledback 665
 T Throttle throttle_speed_in: packet of ~758b  (from 758 b) Speed AVG=   0[w=1]    0[w=1] /  Limit=16 KiB/sec  [1296 0 0 0 0 0 0 0 0 0 ]
 T Throttle <<< global-IN: packet of ~758b  (from 758 b) Speed AVG=   0[w=9.425]    0[w=9.425] /  Limit=16 KiB/sec  [1296 0 496 0 0 0 0 0 0 0 ]
 T HTTP HEAD:
 T Host: host.example.com:38083
 T User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0
 T Accept: */*
 T Accept-Language: en,da;q=0.5
 T Accept-Encoding: gzip, deflate
 T Content-Type: application/json; charset=utf-8
 T Authorization: Digest username="rpc_user", realm="monero-rpc", nonce="TxiQqEIvHWWbjks8rvdMRA==", uri="/", algorithm="MD5", response="34638b90a7ae09644c8559fa7e1dc4a4", qop="auth", nc=00000001, cnonce="bd5fd9b093dccaa1"
 T Content-Length: 61
 T Origin: http://host.example.com
 T DNT: 1
 T Connection: keep-alive
 T Referer: http://host.example.com/test-05/
 T Pragma: no-cache
 T Cache-Control: no-cache
 T 
 I HTTP [client.ip.addr] POST /json_rpc
 I [client.ip.addr:34239 INC] Calling RPC method get_version
 D /json_rpc[get_version] processed with 0/0/0ms
 T HTTP_RESPONSE_HEAD: << 
 T HTTP/1.1 200 Ok
 T Server: Epee-based
 T Content-Length: 104
 T Content-Type: application/json
 T Last-Modified: Thu, 19 Mar 2020 14:55:14 GMT
 T Accept-Ranges: bytes
 T Access-Control-Allow-Origin: http://host.example.com
 T Access-Control-Expose-Headers: www-authenticate
 T Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS
 T 
 T Throttle throttle_speed_out: packet of ~434b  (from 434 b) Speed AVG=   0[w=1]    0[w=1] /  Limit=16 KiB/sec  [1099 0 0 0 0 0 0 0 0 0 ]
 D do_send_chunk() NOW SENSD: packet=434 B
 T Setting 00:05:00 expiry
 T Setting 00:05:00 expiry
 T [client.ip.addr:34239 INC] [sock 9] Async send calledback 434
 T [sock 8] Some not success at read: End of file:2
 T [sock 8] peer closed connection
 T Closed connection from host client.ip.addr: 2
 T [sock 8] Socket destroyed
 D Destructing connection #6 to client.ip.addr
 T [sock 9] Some not success at read: End of file:2
 T [sock 9] peer closed connection
 T Closed connection from host client.ip.addr: 1
 T [sock 9] Socket destroyed
 D Destructing connection #7 to client.ip.addr

Certificate trusted by Firefox:

enter image description here

The code that does the talking:

function httpClient (config, callback) {
    let xhr = new XMLHttpRequest();

    xhr.onload = () => {
      //Step 4: Receive original request data
      if (xhr.status == 200) {
        callback({
          result: xhr.status,
          message: xhr.statusText,
          response: xhr.responseText
          })
      } else {
        callback({
          result: xhr.status,
          message: xhr.statusText,
          response: xhr.responseText
          })
      }
    };

    xhr.onerror = (e) => {
      console.log("Error:", e);
      console.log({
        result: xhr.status,
        message: xhr.statusText,
        response: xhr.responseText
      });
      callback({
          result: xhr.status,
          message: xhr.statusText,
          response: 'Some error ocurred during this request.'
        })
    };

    xhr.onreadystatechange = (e) => {
      if (xhr.readyState >= 2 && xhr.status == 401) {
        console.log("readyState:", xhr.readyState, xhr.status, xhr.statusText);

        let authHeader = xhr.getResponseHeader('www-authenticate');
        let authObject = parseResponseHeader(authHeader);
        let authResponse = buildAuthorizeHeader(config.auth, authObject);
        config.headers["Authorization"] = authResponse;
        xhr.abort();
        httpClient(config, callback);
      }
      else
        console.log("readyState:", xhr.readyState, xhr.status, xhr.statusText);
    };

    config.protocol = config.protocol.replace(/\:$/, '');

    let encodedURI = encodeURI(config.protocol + '://' + config.url);
    xhr.open(config.method, encodedURI, true);
    xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');

    for (let [key, value] of Object.entries(config.headers)) {
      xhr.setRequestHeader(key, value);
    }

    xhr.withCredentials = false;
    if (config.method.toUpperCase() == 'POST' && typeof config.data == "object" ) {
      xhr.send(JSON.stringify(config.data))
    } else {
      xhr.send()
    }

    const buildAuthorizeHeader = (config, tokensObj ) => {
      tokensObj.headers["cnonce"] = 'bd5fd9b093dccaa1';
      tokensObj.headers["nc"] = '00000001';
      tokensObj.headers["algorithm"] = 'MD5';
      tokensObj.headers["method"] = 'POST';
      tokensObj.headers["domain"] = '/';

      let HA1 = CryptoJS.MD5(config.user + ':' + tokensObj.headers["realm"] + ':' + config.pass).toString();
      let HA2 = CryptoJS.MD5(tokensObj.headers["method"] + ':' + tokensObj.headers["domain"]).toString();
      let authResponse = CryptoJS.MD5(HA1 + 
        ':' + tokensObj.headers["nonce"] + 
        ':' + tokensObj.headers["nc"] + 
        ':' + tokensObj.headers["cnonce"] + 
        ':' + tokensObj.headers["qop"] + 
        ':' + HA2).toString();

      let responseContentHeader = 'Digest username="' + config.user + '"' + 
        ', realm="' + tokensObj.headers["realm"] + '"' + 
        ', nonce="' + tokensObj.headers["nonce"] + '"' + 
        ', uri="' + tokensObj.headers["domain"] + '"' + 
        ', algorithm="' + tokensObj.headers["algorithm"] + '"' +
        ', response="' + authResponse + '"' + 
        ', qop="' + tokensObj.headers["qop"] + '"' + 
        ', nc=' + tokensObj.headers["nc"] + 
        ', cnonce="' + tokensObj.headers["cnonce"] + '"';

      return responseContentHeader;
    }

    const parseResponseHeader = (h) => {
      let auth = {
        headers : {}
      };
      var scre = /^\w+/;
      var scheme = scre.exec(h);
      auth.scheme = scheme[0];

      var nvre = /(\w+)=['"]([^'"]+)['"]/g;
      var pairs = h.match(nvre);

      var vre = /(\w+)=['"]([^'"]+)['"]/;
      var i = 0;
      for (; i < pairs.length; i++) {
        var v = vre.exec(pairs[i]);
        if (v) {
          auth.headers[v[1]] = v[2];
        }
      }
      return auth;
    }

}
  • Are you setting --rpc-access-control-origins to * or some.ip.address? – jtgrassie Mar 19 at 5:55
  • No, it's a list of possible clients: --rpc-access-control-origins 'http://site1.example.com,https://site1.example.com,http://site2.example.com,https://site2.example.com' Edit: There's no way of knowing who's accessing the web-site. – Mogens TrasherDK Mar 19 at 6:05
  • CORS relates to the host running your site, not the client browsing your site. Thus * means any host. If the js file (calling the wallet RPC) was hosted at https://example.com/site.js, you would need to run the wallet RPC with --rpc-access-control-origins example.com or --rpc-access-control-origins *. – jtgrassie Mar 19 at 6:48
  • The --rpc-access-control-origins list the websites hosting the js. The client.ip.address in the log is the browser accessing the website. – Mogens TrasherDK Mar 19 at 11:44
  • setting --rpc-access-control-origins '*' makes no difference. Same output in the log. – Mogens TrasherDK Mar 19 at 11:55
1

You are getting a CORS error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://server.ip.address:38083/json_rpc. (Reason: CORS request did not succeed).

The wallet RPC has a parameter --rpc-access-control-origins which you will need to set to the domain hosting your javascript.

From the help:

  --rpc-access-control-origins arg      Specify a comma separated list of 
                                        origins to allow cross origin resource 
                                        sharing

Thus if the site hosting the javascript is https://www.example.com, run the monero-wallet-rpc with --rpc-access-control-origins https://www.example.com.

Aside from this, and dependent on your browser settings, you may need to provide your own SSL certificate at wallet startup (via the --rpc-ssl-... wallet RPC parameters), as the browser may not like the auto-generated certificates the wallet creates. For example, some browsers strictly prohibit self-signed certificates.

|improve this answer|||||
  • I've tried with certificates. Website has a Let's Encrypt certificate, and wallet-rpc has (self signed) certificates, signed by a root the browser trust. It's wallet-rpc who's complaining, not the browser: SSL handshake failed, connection dropped: short read and Some not success at read: sslv3 alert bad certificate:336151570 – Mogens TrasherDK Mar 19 at 14:48
  • The error in Firefox Cross-Origin Request Blocked is a generic response, saying I have no clue about what happened, but it's probably CORS. The HTTP website combined with no certificates at the monero end works. – Mogens TrasherDK Mar 19 at 14:52
  • "is a generic response," < not really that generic, it failed in the CORS preflight. Also, see my last paragraph/sentence. – jtgrassie Mar 19 at 21:50
  • You are kind of right in it failed in the CORS preflight. The browser sends the OPTIONS request, but never get an answer back from wallet-rpc. The log says: SSL handshake failed, connection dropped: wrong version number. Nothing is returned to the browser. – Mogens TrasherDK Mar 20 at 12:22
  • The browser tries 10 times to connect, before giving up and issue a Cross-Origin Request Blocked. There is never a response from wallet-rpc. – Mogens TrasherDK Mar 20 at 12:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.