diff --git a/bubble-server/src/main/java/bubble/ApiConstants.java b/bubble-server/src/main/java/bubble/ApiConstants.java index c577c0e6..5cff989c 100644 --- a/bubble-server/src/main/java/bubble/ApiConstants.java +++ b/bubble-server/src/main/java/bubble/ApiConstants.java @@ -87,6 +87,7 @@ public class ApiConstants { public static final String EP_FORGOT_PASSWORD = "/forgotPassword"; public static final String EP_CHANGE_PASSWORD = "/changePassword"; public static final String EP_CA_CERT = "/cacert"; + public static final String EP_KEY = "/key"; public static final String EP_SCRIPT = "/script"; public static final String EP_APPROVE = "/approve"; public static final String EP_DENY = "/deny"; diff --git a/bubble-server/src/main/java/bubble/resources/account/AuthResource.java b/bubble-server/src/main/java/bubble/resources/account/AuthResource.java index 9871a871..35279820 100644 --- a/bubble-server/src/main/java/bubble/resources/account/AuthResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/AuthResource.java @@ -10,12 +10,14 @@ import bubble.dao.account.AccountPolicyDAO; import bubble.dao.account.message.AccountMessageDAO; import bubble.dao.bill.BubblePlanDAO; import bubble.dao.cloud.BubbleNodeDAO; +import bubble.dao.cloud.BubbleNodeKeyDAO; import bubble.model.CertType; import bubble.model.account.*; import bubble.model.account.message.*; import bubble.model.boot.ActivationRequest; import bubble.model.cloud.BubbleNetwork; import bubble.model.cloud.BubbleNode; +import bubble.model.cloud.BubbleNodeKey; import bubble.model.cloud.NetworkKeys; import bubble.model.cloud.notify.NotificationReceipt; import bubble.model.device.Device; @@ -85,6 +87,7 @@ public class AuthResource { @Autowired private StandardAuthenticatorService authenticatorService; @Autowired private PromotionService promoService; @Autowired private DeviceIdService deviceIdService; + @Autowired private BubbleNodeKeyDAO nodeKeyDAO; public Account updateLastLogin(Account account) { return accountDAO.update(account.setLastLogin()); } @@ -484,6 +487,16 @@ public class AuthResource { return send(new FileSendableResource(certFile).setForceDownload(true)); } + @GET @Path(EP_KEY) + public Response getNodeKey(@Context Request req, + @Context ContainerRequest ctx) { + final BubbleNode thisNode = configuration.getThisNode(); + if (thisNode == null) return notFound(); + final BubbleNodeKey key = nodeKeyDAO.findFirstByNode(thisNode.getUuid()); + if (key == null) return notFound(thisNode.id()); + return ok(key); + } + private Account validateCallerForApproveOrDeny(Account caller, AccountMessage message, String token) { if (message == null) throw notFoundEx(token); diff --git a/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java b/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java index 7653a340..f67d8acf 100644 --- a/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java +++ b/bubble-server/src/main/java/bubble/rule/bblock/BubbleBlockRuleDriver.java @@ -124,6 +124,7 @@ public class BubbleBlockRuleDriver extends TrafficAnalyticsRuleDriver { if (refererURI == null) { if (log.isInfoEnabled()) log.info(prefix+"invalid referer ("+filter.getReferer()+")"); } else { + if (log.isInfoEnabled()) log.info(prefix+"decision for URL was ALLOW, checking against referer: host="+refererURI.getHost()+", path="+refererURI.getPath()); final BlockDecision refererDecision = getDecision(refererURI.getHost(), refererURI.getPath(), filter.getUserAgent()); switch (refererDecision.getDecisionType()) { case block: diff --git a/bubble-server/src/main/java/bubble/service/boot/StandardSelfNodeService.java b/bubble-server/src/main/java/bubble/service/boot/StandardSelfNodeService.java index a0d69d27..51698d41 100644 --- a/bubble-server/src/main/java/bubble/service/boot/StandardSelfNodeService.java +++ b/bubble-server/src/main/java/bubble/service/boot/StandardSelfNodeService.java @@ -25,6 +25,9 @@ import bubble.service.notify.NotificationService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.cobbzilla.util.cache.Refreshable; +import org.cobbzilla.util.http.HttpSchemes; +import org.cobbzilla.util.http.HttpUtil; +import org.cobbzilla.util.io.FileUtil; import org.cobbzilla.util.string.StringUtil; import org.cobbzilla.util.system.OneWayFlag; import org.springframework.beans.factory.annotation.Autowired; @@ -36,8 +39,8 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static bubble.ApiConstants.HOME_DIR; -import static bubble.ApiConstants.NULL_NODE; +import static bubble.ApiConstants.*; +import static bubble.ApiConstants.EP_KEY; import static bubble.model.cloud.BubbleNode.nodeFromFile; import static bubble.model.cloud.BubbleNodeKey.nodeKeyFromFile; import static bubble.server.BubbleServer.disableRestoreMode; @@ -226,25 +229,20 @@ public class StandardSelfNodeService implements SelfNodeService { } private BubbleNode initSageNode(BubbleNode selfNode) { - final BubbleNode sage = nodeDAO.findByUuid(selfNode.getSageNode()); + BubbleNode sage = nodeDAO.findByUuid(selfNode.getSageNode()); if (sage == null) { // do we have a local file we can fall back on? if (!SAGE_NODE_FILE.exists()) { log.warn("initSageNode: DB contains no entry for selfNode.sage ("+selfNode.getSageNode()+") and "+abs(SAGE_NODE_FILE)+ " does not exist, returning null"); return NULL_NODE; } - return ensureSageKeyExists(syncSage(selfNode, nodeFromFile(SAGE_NODE_FILE))); + sage = syncSage(selfNode, nodeFromFile(SAGE_NODE_FILE)); } - return ensureSageKeyExists(syncSage(selfNode, SAGE_NODE_FILE.exists() + sage = syncSage(selfNode, SAGE_NODE_FILE.exists() ? nodeFromFile(SAGE_NODE_FILE) - : sage)); - } - - private BubbleNode ensureSageKeyExists(BubbleNode sageNode) { - final BubbleNodeKey sageKey = initSageKey(sageNode); - return sageKey != null - ? sageNode - : die("finalizeRestore: no sage key found in DB and no sage key file, cannot finalize restore. Sage = "+sageNode.id()); + : sage); + initSageKey(sage); + return sage; } private BubbleNode syncSage(BubbleNode selfNode, BubbleNode sage) { @@ -359,13 +357,14 @@ public class StandardSelfNodeService implements SelfNodeService { } // try key from file - if (!SAGE_KEY_FILE.exists()) return null; + if (!SAGE_KEY_FILE.exists()) return fetchLatestSageKey(sageNode); sageKey = nodeKeyFromFile(SAGE_KEY_FILE); final BubbleNodeKey existingByUuid = nodeKeyDAO.findByUuid(sageKey.getUuid()); final BubbleNodeKey existingByHash = nodeKeyDAO.findByPublicKeyHash(sageKey.getPublicKeyHash()); if (existingByUuid == null && existingByHash == null) { if (sageKey.expired()) { - return die("initSageKey: key not found in DB, but key on disk has expired: "+sageKey); + log.warn("initSageKey: key not found in DB and key on disk has expired, re-keying: "+sageKey); + return fetchLatestSageKey(sageNode); } return nodeKeyDAO.create(sageKey); @@ -373,7 +372,8 @@ public class StandardSelfNodeService implements SelfNodeService { if (!existingByHash.getUuid().equals(existingByUuid.getUuid())) { // should never happen, but reset just in case if (sageKey.expired()) { - return die("initSageKey: key found in DB with different entries for uuid/fqdn, and key on disk has expired: "+sageKey); + log.warn("initSageKey: key not found in DB and key on disk has expired, re-keying: "+sageKey); + return fetchLatestSageKey(sageNode); } nodeKeyDAO.delete(existingByUuid.getUuid()); nodeKeyDAO.delete(existingByHash.getUuid()); @@ -391,6 +391,21 @@ public class StandardSelfNodeService implements SelfNodeService { } } + private BubbleNodeKey fetchLatestSageKey(BubbleNode sageNode) { + log.warn("fetchLatestSageKey: key found in DB with different entries for uuid/fqdn, and key on disk has expired, refreshing: "+sageNode.id()); + try { + final String keyUrl = HttpSchemes.SCHEME_HTTPS + sageNode.getFqdn() + ":" + sageNode.getSslPort() + configuration.getHttp().getBaseUri() + AUTH_ENDPOINT + EP_KEY; + log.info("fetchLatestSageKey: fetching sage key from: "+keyUrl); + final String keyJson = HttpUtil.url2string(keyUrl); + final BubbleNodeKey sageKey = json(keyJson, BubbleNodeKey.class); + FileUtil.toFile(SAGE_KEY_FILE, keyJson); + nodeKeyDAO.create(sageKey); + return sageKey; + } catch (Exception e) { + return die("fetchLatestSageKey: error fetching/saving latest sage key: "+e, e); + } + } + @Override public BubblePlan getThisPlan() { final BubbleNetwork network = safeGetThisNetwork(); if (network == null) return null;