Haga que la conexión RPC remota al servidor bitcoind sea segura

Configuré un servidor bitcoind en un servidor Ubuntu y puedo ejecutar comandos de forma remota en el servidor. Estoy haciendo esto con Java/RPC. Para ejecutar un comando, necesito proporcionar un nombre de usuario y una contraseña. Pero creo que eso no sería suficiente seguridad. Por eso pregunto aquí. ¿Cómo puedo hacer que esta conexión sea realmente segura? Primero, pensé que solo podía permitir solicitudes de una IP específica, pero eso no funcionará porque mi aplicación se ejecuta en App Engine de Google y, por lo tanto, no tiene una IP estática.

¿Alguna idea de cómo hacer esto seguro? Aquí está mi código, si es de su interés:

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

public class RPCClient {

    private static final String COMMAND_GET_BALANCE = "getbalance";
    private static final String COMMAND_GET_INFO = "getinfo";
    private static final String COMMAND_GET_NEW_ADDRESS = "getnewaddress";

    private JSONObject invokeRPC(String id, String method, List<String> params) {

//      CloseableHttpClient httpclient = HttpClientBuilder.create().build();
//      
//      httpclient.getCredentialsProvider();
        DefaultHttpClient httpclient = new DefaultHttpClient();

        JSONObject json = new JSONObject();
        json.put("id", id);
        json.put("method", method);
        if (null != params) {
            JSONArray array = new JSONArray();
            array.addAll(params);
            json.put("params", params);
        }
        JSONObject responseJsonObj = null;
        try {
            httpclient.getCredentialsProvider().setCredentials(new AuthScope("55.233.188.139", 9332),
                    new UsernamePasswordCredentials("myUser", "mySuperSecurePW"));
            StringEntity myEntity = new StringEntity(json.toJSONString());
            System.out.println(json.toString());
            HttpPost httppost = new HttpPost("http://55.233.188.139:9332");
            httppost.setEntity(myEntity);

            System.out.println("executing request" + httppost.getRequestLine());
            HttpResponse response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();

            System.out.println("----------------------------------------");
            System.out.println(response.getStatusLine());
            if (entity != null) {
                System.out.println("Response content length: " + entity.getContentLength());
                // System.out.println(EntityUtils.toString(entity));
            }
            JSONParser parser = new JSONParser();
            responseJsonObj = (JSONObject) parser.parse(EntityUtils.toString(entity));
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (org.json.simple.parser.ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
            httpclient.getConnectionManager().shutdown();
        }
        return responseJsonObj;
    }

    public Double getBalance(String account) {
        String[] params = { account };
        JSONObject json = invokeRPC(UUID.randomUUID().toString(), COMMAND_GET_BALANCE, Arrays.asList(params));
        return (Double)json.get("result");
    }

    public String getNewAddress(String account) {
        String[] params = { account };
        JSONObject json = invokeRPC(UUID.randomUUID().toString(), COMMAND_GET_NEW_ADDRESS, Arrays.asList(params));
        return (String)json.get("result");
    }

    public JSONObject getInfo() {
        JSONObject json = invokeRPC(UUID.randomUUID().toString(), COMMAND_GET_INFO, null);
        return (JSONObject)json.get("result");
    }

    public JSONObject getInfo(String command) {
        JSONObject json = invokeRPC(UUID.randomUUID().toString(), command, null);
        return (JSONObject)json.get("result");
    }

    public static void main(String[] args) {
        System.out.println(new RPCClient().getInfo());      
    }
}
Primero dijiste que tenías un servidor Ubuntu con bitcoind ejecutándose y luego AppEngine. ¿Cuál es?

Respuestas (2)

EDITAR : acabo de notar que su pregunta solicita una solución específica de App Engine. Esta solución solo se aplica si tiene el 100% del control sobre el servidor Linux que ejecuta su código, por lo que esto no funciona en App Engine (o no tengo idea si hoy en día tiene la capacidad de construir túneles). En cualquier caso, le recomiendo que mueva su código a un servidor virtual desde el motor de la aplicación para tener más control sobre su pila, ya que podría alcanzar las limitaciones de App Engine bastante rápido si sondea bitcoind para transacciones.

Cree un túnel TCP/IP a través de SSH desde la computadora que ejecuta su código hasta el servidor que ejecuta bitcoind.

Una manera fácil de construir un túnel de este tipo que se reinicie al desconectarse es AutoSSH .

Aquí hay un script de ejemplo de AutoSSH:

http://www.ubuntugeek.com/automatically-restart-ssh-sessions-and-tunnels-using-autossh.html

Recomendaciones:

  1. Cree una API-REST, WS o cualquier equivalente para la comunicación entre el nodo y el exterior.

  2. Limite el acceso al nodo RPC tanto como sea posible (Acceda al nodo solo localmente), debe considerar el nodo bitcoin como una base de datos. use su servidor ubuntu solo para ejecutar RPC y el servicio externo.

  3. Nunca exponga una conexión al nodo al exterior. Consulte la documentación de Bitcoin RPC para obtener más información.

https://bitcoincore.org/en/doc/

Lo siento, mi corrector automático me hizo creer que este es el foro en español.