소스 검색

Merge branch 'master' into kris/delete_account

# Conflicts:
#	bubble-web
pull/55/head
Kristijan Mitrovic 4 년 전
부모
커밋
f3636d1149
49개의 변경된 파일656개의 추가작업 그리고 101개의 파일을 삭제
  1. +23
    -0
      bin/build_dist
  2. +15
    -4
      bin/first_time_ubuntu.sh
  3. +2
    -2
      bin/install_packer.sh
  4. +16
    -0
      bin/reset_bubble_logs
  5. +5
    -5
      bubble-server/src/main/java/bubble/cloud/compute/PackerImageParserBase.java
  6. +1
    -1
      bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java
  7. +1
    -1
      bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java
  8. +6
    -6
      bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java
  9. +8
    -0
      bubble-server/src/main/java/bubble/dao/app/AppMatcherDAO.java
  10. +3
    -0
      bubble-server/src/main/java/bubble/model/cloud/BubbleVersionInfo.java
  11. +5
    -2
      bubble-server/src/main/java/bubble/resources/cloud/NetworksResource.java
  12. +1
    -1
      bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java
  13. +28
    -2
      bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java
  14. +9
    -3
      bubble-server/src/main/java/bubble/server/BubbleConfiguration.java
  15. +3
    -2
      bubble-server/src/main/java/bubble/server/BubbleServer.java
  16. +141
    -0
      bubble-server/src/main/java/bubble/server/SoftwareVersions.java
  17. +3
    -5
      bubble-server/src/main/java/bubble/service/boot/StandardSelfNodeService.java
  18. +1
    -1
      bubble-server/src/main/java/bubble/service/cloud/AnsiblePrepService.java
  19. +14
    -8
      bubble-server/src/main/java/bubble/service/dbfilter/DatabaseFilterService.java
  20. +17
    -3
      bubble-server/src/main/java/bubble/service/packer/PackerJob.java
  21. +16
    -2
      bubble-server/src/main/java/bubble/service/packer/PackerService.java
  22. +2
    -0
      bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java
  23. +1
    -1
      bubble-server/src/main/resources/META-INF/bubble/bubble.properties
  24. +2
    -1
      bubble-server/src/main/resources/ansible/bubble_scripts.txt
  25. +1
    -0
      bubble-server/src/main/resources/ansible/roles/finalizer/files/bubble_role.json
  26. +5
    -0
      bubble-server/src/main/resources/ansible/roles/finalizer/tasks/main.yml
  27. +1
    -0
      bubble-server/src/main/resources/ansible/roles/mitmproxy/files/supervisor_mitm_monitor.conf
  28. +1
    -0
      bubble-server/src/main/resources/bubble-config.yml
  29. +2
    -2
      bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs
  30. +102
    -0
      bubble-server/src/main/resources/bubble/rule/social/block/site/LI.js.hbs
  31. +23
    -0
      bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li.json
  32. +12
    -0
      bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_data.json
  33. +15
    -0
      bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_matchers.json
  34. +3
    -0
      bubble-server/src/main/resources/models/manifest-app-user-block.json
  35. +2
    -2
      bubble-server/src/main/resources/packer/roles/algo/tasks/main.yml
  36. +7
    -1
      bubble-server/src/main/resources/packer/roles/bubble/files/refresh_bubble_ssh_keys.sh
  37. +8
    -0
      bubble-server/src/main/resources/packer/roles/bubble/tasks/main.yml
  38. +6
    -0
      bubble-server/src/main/resources/packer/roles/bubble/templates/bubble_versions.properties.j2
  39. +37
    -0
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/_client.py
  40. +30
    -0
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/_events.py
  41. +2
    -2
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_api.py
  42. +6
    -8
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py
  43. +19
    -20
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_request.py
  44. +14
    -7
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/mitm_monitor.sh
  45. +30
    -2
      bubble-server/src/main/resources/packer/roles/mitmproxy/files/run_mitm.sh
  46. +4
    -4
      bubble-server/src/main/resources/packer/roles/mitmproxy/tasks/main.yml
  47. +1
    -1
      bubble-web
  48. +1
    -1
      utils/cobbzilla-utils
  49. +1
    -1
      utils/cobbzilla-wizard

+ 23
- 0
bin/build_dist 파일 보기

@@ -52,9 +52,32 @@ ZIP="${DIST_BASE}/bubble-${VERSION}.zip"
mkdir -p "${DIST}" || die "Error creating distribution directory: ${DIST}"
cp "${JAR}" "${DIST}/bubble.jar" || die "Error copying ${JAR} to ${DIST}/bubble.jar"
cp "${BASE}/README.md" "${DIST}/README.md" || die "Error copying README.md to ${DIST}/README.md"
cp "${BASE}/LICENSE.md" "${DIST}/LICENSE.md" || die "Error copying LICENSE.md to ${DIST}/LICENSE.md"
cp -R "${BASE}/docs" "${DIST}" || die "Error copying docs directory to ${DIST}"
cp -R "${BASE}/bin" "${DIST}" || die "Error copying bin directory to ${DIST}"
cp -R "${BASE}/config" "${DIST}" || die "Error copying config directory to ${DIST}"
cd "${DIST}/.." && zip -r "${ZIP}" "$(basename ${DIST})"
echo "Distribution created: "
ls -lh "${ZIP}"

if [[ ! -z "${BUBBLE_DIST_HOME}" ]] ; then
IS_DEV=0
if [[ -z ${BUILD_NUMBER} ]] ; then
BUILD_NUMBER="dev"
IS_DEV=1
fi
BUBBLE_VERSION="${VERSION}.${BUILD_NUMBER}"

BUBBLE_DIST_TOP=${BUBBLE_DIST_HOME}/releases/bubble
BUBBLE_DIST=${BUBBLE_DIST_TOP}/${BUBBLE_VERSION}/$(basename ${ZIP})
BUBBLE_DIST_DIR="$(dirname ${BUBBLE_DIST})"
if [[ ! -d "${BUBBLE_DIST_DIR}" ]] ; then
mkdir -p ${BUBBLE_DIST_DIR}
fi
cp "${ZIP}" "${BUBBLE_DIST}" && cat "${ZIP}" | sha256sum | cut -f1 -d' ' | tr -d '\n' > "${BUBBLE_DIST}.sha256"
if [[ ${IS_DEV} -eq 0 ]] ; then
cd ${BUBBLE_DIST_TOP} && rm -f latest && ln -sf ${BUBBLE_VERSION} latest
echo "${BUBBLE_VERSION}" > latest.txt
fi
echo "Published release: ${BUBBLE_DIST}"
fi

+ 15
- 4
bin/first_time_ubuntu.sh 파일 보기

@@ -7,6 +7,16 @@ function die {
exit 1
}

function db_user_exists {
username="${1}"
num_users="$(echo "select count(*) from pg_user where usename='${username}'" | su - postgres psql -qt | egrep -v '^$')"
if [[ -z "${num_users}" || ${num_users} -eq 0 ]] ; then
echo "0"
else
echo "1"
fi
}

# Ensure system is current
sudo apt update -y || die "Error running apt update"
sudo apt upgrade -y || die "Error running apt upgrade"
@@ -21,7 +31,11 @@ BUBBLE_BIN="$(cd "$(dirname "${0}")" && pwd)"

# Create DB user for current user, as superuser
CURRENT_USER="$(whoami)"
sudo su - postgres bash -c 'createuser -U postgres --createdb --createrole --superuser '"${CURRENT_USER}"'' || die "Error creating ${CURRENT_USER} DB user"
if [[ $(db_user_exists ${CURRENT_USER}) ]] ; then
echo "PostgreSQL user ${CURRENT_USER} already exists, not creating"
else
sudo su - postgres bash -c 'createuser -U postgres --createdb --createrole --superuser '"${CURRENT_USER}"'' || die "Error creating ${CURRENT_USER} DB user"
fi

PG_HBA=$(find /etc/postgresql -mindepth 1 -maxdepth 1 -type d | sort | tail -1)/main/pg_hba.conf
sudo cat ${PG_HBA} | sed -e 's/ peer/ trust/g' | sed -e 's/ md5/ trust/g' > /tmp/pg_hba.conf || die "Error filtering ${PG_HBA}"
@@ -30,6 +44,3 @@ sudo service postgresql restart || die "Error restarting pgsql"

# Create DB user 'bubble', with the ability to create databases
createuser --createdb bubble || die "Error creating bubble DB user"

# Create bubble database
createdb --encoding=UTF-8 bubble || die "Error creating bubble DB"

+ 2
- 2
bin/install_packer.sh 파일 보기

@@ -9,7 +9,7 @@ function die {

# Install packer
if [[ ! -f ${HOME}/packer/packer ]] ; then
PACKER_VERSION=1.5.6
PACKER_VERSION=1.6.2
PACKER_FILE=packer_${PACKER_VERSION}_linux_amd64.zip
PACKER_URL=https://releases.hashicorp.com/packer/${PACKER_VERSION}/${PACKER_FILE}
mkdir -p ${HOME}/packer && cd ${HOME}/packer && wget ${PACKER_URL} && unzip ${PACKER_FILE} || die "Error installing packer"
@@ -19,7 +19,7 @@ fi

# Install packer Vultr plugin
if [[ ! -f ${HOME}/.packer.d/plugins/packer-builder-vultr ]] ; then
PACKER_VULTR_VERSION=1.0.8
PACKER_VULTR_VERSION=1.0.11
PACKER_VULTR_FILE=packer-builder-vultr_${PACKER_VULTR_VERSION}_linux_64-bit.tar.gz
PACKER_VULTR_URL=https://github.com/vultr/packer-builder-vultr/releases/download/v${PACKER_VULTR_VERSION}/${PACKER_VULTR_FILE}
mkdir -p ${HOME}/.packer.d/plugins && cd ${HOME}/.packer.d/plugins && wget ${PACKER_VULTR_URL} && tar xzf ${PACKER_VULTR_FILE} || die "Error installing packer vultr plugin"


+ 16
- 0
bin/reset_bubble_logs 파일 보기

@@ -0,0 +1,16 @@
#!/bin/bash
#
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
#
#
# Truncate all bubble logs
#
BUBBLE_LOGS_DIR=/var/log/bubble
if [[ ! -d ${BUBBLE_LOGS_DIR} ]] ; then
echo "BUBBLE_LOGS_DIR not found, nothing to truncate: ${BUBBLE_LOGS_DIR}"
exit 0
fi

find ${BUBBLE_LOGS_DIR} -type f | while read log ; do
cat /dev/null > ${log} && echo "truncated ${log}" || echo "error truncating ${log}"
done

+ 5
- 5
bubble-server/src/main/java/bubble/cloud/compute/PackerImageParserBase.java 파일 보기

@@ -8,18 +8,18 @@ import static bubble.service.packer.PackerJob.PACKER_IMAGE_PREFIX;

public abstract class PackerImageParserBase extends ListResourceParser<PackerImage> {

private String bubbleVersion;
private String keyHash;
private final String bubbleVersion;
private final String versionHash;

public PackerImageParserBase(String bubbleVersion, String keyHash) {
public PackerImageParserBase(String bubbleVersion, String versionHash) {
this.bubbleVersion = bubbleVersion;
this.keyHash = keyHash;
this.versionHash = versionHash;
}

public boolean isValidPackerImage(String name) {
if (!name.startsWith(PACKER_IMAGE_PREFIX)) return false;
if (!name.contains("_"+bubbleVersion+"_")) return false;
if (!name.contains("_"+keyHash+"_")) return false;
if (!name.contains("_"+versionHash+"_")) return false;
return true;
}



+ 1
- 1
bubble-server/src/main/java/bubble/cloud/compute/digitalocean/DigitalOceanDriver.java 파일 보기

@@ -235,7 +235,7 @@ public class DigitalOceanDriver extends ComputeServiceDriverBase {
@Override public List<PackerImage> getPackerImagesForRegion(String region) { return getPackerImages(); }

public List<PackerImage> getPackerImages () {
final List<PackerImage> images = getResources(PACKER_IMAGES_URI, new DigitalOceanPackerImageParser(configuration.getShortVersion(), packerService.getPackerPublicKeyHash()));
final List<PackerImage> images = getResources(PACKER_IMAGES_URI, new DigitalOceanPackerImageParser(configuration.getShortVersion(), packerService.getPackerVersionHash()));
return images == null ? Collections.emptyList() : images;
}



+ 1
- 1
bubble-server/src/main/java/bubble/cloud/compute/ec2/AmazonEC2Driver.java 파일 보기

@@ -183,7 +183,7 @@ public class AmazonEC2Driver extends ComputeServiceDriverBase {
final ArrayList<Filter> filters = new ArrayList<>();
filters.add(new Filter("root-device-type", new SingletonList<>("ebs")));
filters.add(new Filter("state", new SingletonList<>("available")));
filters.add(new Filter("name", new SingletonList<>("packer_*_"+packerService.getPackerPublicKeyHash()+"_"+configuration.getShortVersion()+"_*")));
filters.add(new Filter("name", new SingletonList<>("packer_*_"+packerService.getPackerVersionHash()+"_"+configuration.getShortVersion()+"_*")));
final AmazonEC2 ec2 = getEc2Client(region);
final DescribeImagesRequest imageRequest = new DescribeImagesRequest().withFilters(filters);
final DescribeImagesResult imagesResult = ec2.describeImages(imageRequest);


+ 6
- 6
bubble-server/src/main/java/bubble/cloud/compute/vultr/VultrDriver.java 파일 보기

@@ -410,7 +410,7 @@ public class VultrDriver extends ComputeServiceDriverBase {
@Override public List<PackerImage> getPackerImagesForRegion(String region) { return getPackerImages(); }

public List<PackerImage> getPackerImages () {
final List<PackerImage> images = loadCloudResources(SNAPSHOT_URL, new VultrPackerImageParser(configuration.getShortVersion(), packerService.getPackerPublicKeyHash()));
final List<PackerImage> images = loadCloudResources(SNAPSHOT_URL, new VultrPackerImageParser(configuration.getShortVersion(), packerService.getPackerVersionHash()));
return images == null ? Collections.emptyList() : images;
}

@@ -424,16 +424,16 @@ public class VultrDriver extends ComputeServiceDriverBase {
}

// wait longer for the snapshot...
final String keyHash = packerService.getPackerPublicKeyHash();
final String versionHash = packerService.getPackerVersionHash();
final long start = now();
PackerImage snapshot = null;
while (now() - start < SNAPSHOT_TIMEOUT) {
snapshot = getPackerImages().stream()
.filter(i -> i.getName().contains("_"+installType.name()+"_") && i.getName().contains(keyHash))
.filter(i -> i.getName().contains("_"+installType.name()+"_") && i.getName().contains(versionHash))
.findFirst()
.orElse(null);
if (snapshot != null) break;
sleep(SECONDS.toMillis(20), "finalizeIncompletePackerRun: waiting for snapshot: "+keyHash);
sleep(SECONDS.toMillis(20), "finalizeIncompletePackerRun: waiting for snapshot: "+versionHash);
}
if (snapshot == null) {
log.error("finalizeIncompletePackerRun: timeout waiting for snapshot");
@@ -447,14 +447,14 @@ public class VultrDriver extends ComputeServiceDriverBase {

public boolean stopImageServer(AnsibleInstallType installType) {

final String keyHash = packerService.getPackerPublicKeyHash();
final String versionHash = packerService.getPackerVersionHash();
final List<BubbleNode> servers;

// find the server(s)
try {
servers = listNodes(server -> {
final String tag = server.has(VULTR_TAG) ? server.get(VULTR_TAG).textValue() : null;
return tag != null && tag.contains("_"+installType.name()+"_") && tag.contains(keyHash);
return tag != null && tag.contains("_"+installType.name()+"_") && tag.contains(versionHash);
});
} catch (IOException e) {
log.error("stopImageServer: error listing servers: "+shortError(e), e);


+ 8
- 0
bubble-server/src/main/java/bubble/dao/app/AppMatcherDAO.java 파일 보기

@@ -67,6 +67,14 @@ public class AppMatcherDAO extends AppTemplateEntityDAO<AppMatcher> {
return super.preCreate(matcher);
}

@Override public AppMatcher postCreate(AppMatcher matcher, Object context) {
final BubbleApp app = appDAO.findByUuid(matcher.getApp());
if (app == null) return die("postCreate("+ matcher.getUuid()+"): app not found: "+ matcher.getApp());
ruleEngineService.flushCaches();
primerService.prime(app);
return super.postCreate(matcher, context);
}

@Override public AppMatcher postUpdate(AppMatcher matcher, Object context) {
final BubbleApp app = appDAO.findByUuid(matcher.getApp());
if (app == null) return die("postUpdate("+ matcher.getUuid()+"): app not found: "+ matcher.getApp());


+ 3
- 0
bubble-server/src/main/java/bubble/model/cloud/BubbleVersionInfo.java 파일 보기

@@ -10,6 +10,8 @@ import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.util.Properties;

import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.wizard.model.SemanticVersion.isNewerVersion;

@@ -19,6 +21,7 @@ public class BubbleVersionInfo {
@Getter @Setter private String version;
@Getter @Setter private String shortVersion;
@Getter @Setter private String sha256;
@Getter @Setter private Properties software;

public boolean valid() { return !empty(version) && !empty(sha256); }



+ 5
- 2
bubble-server/src/main/java/bubble/resources/cloud/NetworksResource.java 파일 보기

@@ -217,16 +217,19 @@ public class NetworksResource extends AccountOwnedResource<BubbleNetwork, Bubble
return configuration.subResource(BackupsResource.class, account, network);
}

@Path("/{id}" + EP_LOGS)
@Path("/{id}"+EP_LOGS)
@NonNull public LogsResource getLogs(@NonNull @Context final ContainerRequest ctx,
@NonNull @PathParam("id") String id) {
// caller must be admin
final Account caller = userPrincipal(ctx);
if (!caller.admin()) throw forbiddenEx();

final var network = find(ctx, id);
if (network == null) throw notFoundEx(id);

// only available for this (current) network
if (!configuration.getThisNetwork().getUuid().equals(network.getUuid())) throw forbiddenEx();

final Account caller = userPrincipal(ctx);
return configuration.subResource(LogsResource.class, caller);
}
}

+ 1
- 1
bubble-server/src/main/java/bubble/resources/stream/FilterHttpRequest.java 파일 보기

@@ -43,7 +43,7 @@ public class FilterHttpRequest {
@Getter @Setter private String contentSecurityPolicy;
public boolean hasContentSecurityPolicy () { return !empty(contentSecurityPolicy); }

public static final Pattern NONCE_PATTERN = Pattern.compile("\\s+script-src\\s+.*'nonce-([^']+)'");
public static final Pattern NONCE_PATTERN = Pattern.compile(";\\s*script-src\\s+.*'nonce-([^']+)'");

@Getter(lazy=true) private final String scriptNonce = initScriptNonce();
private String initScriptNonce () {


+ 28
- 2
bubble-server/src/main/java/bubble/resources/stream/FilterHttpResource.java 파일 보기

@@ -36,9 +36,11 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.collection.ExpirationEvictionPolicy;
import org.cobbzilla.util.collection.ExpirationMap;
import org.cobbzilla.util.collection.NameAndValue;
import org.cobbzilla.util.http.HttpContentEncodingType;
import org.cobbzilla.util.http.HttpUtil;
import org.cobbzilla.util.network.NetworkUtil;
import org.cobbzilla.util.string.StringUtil;
import org.cobbzilla.wizard.cache.redis.RedisService;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.jersey.server.ContainerRequest;
@@ -67,6 +69,7 @@ import static org.cobbzilla.util.collection.ArrayUtil.arrayToString;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON;
import static org.cobbzilla.util.http.HttpContentTypes.TEXT_PLAIN;
import static org.cobbzilla.util.http.HttpUtil.applyRegexToUrl;
import static org.cobbzilla.util.json.JsonUtil.COMPACT_MAPPER;
import static org.cobbzilla.util.json.JsonUtil.json;
import static org.cobbzilla.util.network.NetworkUtil.isLocalIpv4;
@@ -695,9 +698,32 @@ public class FilterHttpResource {
public Response followLink(@Context Request req,
@Context ContainerRequest ctx,
@PathParam("requestId") String requestId,
JsonNode urlNode) {
JsonNode followSpec) {
final FilterSubContext filterCtx = new FilterSubContext(req, requestId);
return ok(redirectCache.computeIfAbsent(urlNode.textValue(), HttpUtil::chaseRedirects));

// is this a request to parse regexes from a URL?
if (followSpec.has("regex")) {
return ok(redirectCache.computeIfAbsent(json(followSpec), k -> {
final String url = followSpec.get("url").textValue();
final String regex = followSpec.get("regex").textValue();
final Integer group = followSpec.has("group") ? followSpec.get("group").asInt() : null;
final List<NameAndValue> headers = new ArrayList<>();
for (String name : req.getHeaderNames()) {
final String value = req.getHeader(name);
headers.add(new NameAndValue(name, value));
}
final List<String> matches = applyRegexToUrl(url, headers, regex, group);
return matches == null ? null : StringUtil.toString(matches, "\n");
}));

} else if (followSpec.isTextual()) {
// just a regular follow -- chase redirects
return ok(redirectCache.computeIfAbsent(followSpec.textValue(), HttpUtil::chaseRedirects));
} else {
final String json = json(followSpec);
log.error("followLink: invalid json (expected String or {regex, url}): "+json);
return notFound(json);
}
}

@Path(EP_ASSETS+"/{requestId}/{appId}")


+ 9
- 3
bubble-server/src/main/java/bubble/server/BubbleConfiguration.java 파일 보기

@@ -52,7 +52,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.beans.Transient;
import java.io.File;
import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@@ -98,6 +98,7 @@ public class BubbleConfiguration extends PgRestServerConfiguration
public static final String TAG_SECURITY_LEVELS = "securityLevels";
public static final String TAG_RESTORE_MODE = "awaitingRestore";
public static final String TAG_RESTORING_IN_PROGRESS = "restoreInProgress";
public static final String TAG_FULL_VERSION = "fullVersion";
public static final String TAG_JAR_VERSION = "jarVersion";
public static final String TAG_JAR_UPGRADE_AVAILABLE = "jarUpgradeAvailable";
public static final String TAG_MAX_USERS = "maxUsers";
@@ -166,6 +167,9 @@ public class BubbleConfiguration extends PgRestServerConfiguration

@Getter @Setter private String letsencryptEmail;

@Getter @Setter private String releaseUrlBase;
@Getter(lazy=true) private final SoftwareVersions softwareVersions = new SoftwareVersions(getReleaseUrlBase());

@Setter private String localStorageDir = DEFAULT_LOCAL_STORAGE_DIR;
public String getLocalStorageDir () { return empty(localStorageDir) ? DEFAULT_LOCAL_STORAGE_DIR : localStorageDir; }

@@ -246,7 +250,7 @@ public class BubbleConfiguration extends PgRestServerConfiguration
return SCHEME_HTTPS + node.getFqdn() + ":" + node.getSslPort() + getHttp().getBaseUri();
}

private String getVersion () {
public static String getVersion () {
final Properties properties = new Properties();
try {
properties.load(loadResourceAsStream("META-INF/bubble/bubble.properties"));
@@ -264,7 +268,8 @@ public class BubbleConfiguration extends PgRestServerConfiguration
return new BubbleVersionInfo()
.setVersion(version)
.setShortVersion(shortVersion)
.setSha256(getJarSha());
.setSha256(getJarSha())
.setSoftware(getSoftwareVersions().getDefaultSoftwareVersions());
}
public String getShortVersion () { return getVersionInfo().getShortVersion(); }

@@ -366,6 +371,7 @@ public class BubbleConfiguration extends PgRestServerConfiguration
{TAG_SSL_PORT, getDefaultSslPort()},
{TAG_SUPPORT, getSupport()},
{TAG_SECURITY_LEVELS, DeviceSecurityLevel.values()},
{TAG_FULL_VERSION, getVersionInfo()},
{TAG_JAR_VERSION, getVersion()},
{TAG_JAR_UPGRADE_AVAILABLE, getJarUpgradeAvailable() ? getSageVersion() : null},
{TAG_MAX_USERS, plan == null ? null : plan.getMaxAccounts()},


+ 3
- 2
bubble-server/src/main/java/bubble/server/BubbleServer.java 파일 보기

@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicReference;

import static bubble.ApiConstants.HOME_DIR;
import static bubble.model.cloud.BubbleNode.nodeFromFile;
import static bubble.server.BubbleConfiguration.getVersion;
import static bubble.service.boot.StandardSelfNodeService.THIS_NODE_FILE;
import static org.cobbzilla.util.daemon.ZillaRuntime.die;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
@@ -86,14 +87,14 @@ public class BubbleServer extends RestServerBase<BubbleConfiguration> {

restoreKey.set(getRestoreKey());
if (restoreKey.get() != null) {
log.info("Starting BubbleServer in restore mode...");
log.info("Starting BubbleServer ("+getVersion()+") in restore mode...");
main(BubbleServer.class, RESTORE_LIFECYCLE_LISTENERS, configSource, env);
} else {
if (Boolean.parseBoolean(env.get(BUBBLE_DUMP_CONFIG))) {
log.info("Dumping BubbleConfiguration...");
dumpConfig(configSource);
} else {
log.info("Starting BubbleServer...");
log.info("Starting BubbleServer ("+getVersion()+")...");
main(BubbleServer.class, LIFECYCLE_LISTENERS, configSource, env);
}
}


+ 141
- 0
bubble-server/src/main/java/bubble/server/SoftwareVersions.java 파일 보기

@@ -0,0 +1,141 @@
/**
* Copyright (c) 2020 Bubble, Inc. All rights reserved.
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
*/
package bubble.server;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.io.FileUtil;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import static bubble.ApiConstants.HOME_DIR;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.http.HttpUtil.url2string;

@Slf4j
public class SoftwareVersions {

public static final String ROLE_ALGO = "algo";
public static final String ROLE_MITMPROXY = "mitmproxy";
public static final String ROLE_DNSCRYPT = "dnscrypt-proxy";
public static final String ROLE_BUBBLE = "bubble";
public static final String[] VERSIONED_SOFTWARE = {ROLE_DNSCRYPT, ROLE_ALGO, ROLE_MITMPROXY};

public static final File SOFTWARE_VERSIONS_FILE = new File(HOME_DIR+"/bubble_versions.properties");

public static final String SUFFIX_VERSION = "_version";
public static final String SUFFIX_SHA = "_sha";

private final String releaseUrlBase;

public SoftwareVersions (String releaseUrlBase) { this.releaseUrlBase = releaseUrlBase; }

public String getRolePropBase(String roleName) { return roleName.replace("-", "_"); }

public String getLatestVersion(String r) {
try {
return url2string(releaseUrlBase+"/"+ r +"/latest.txt").trim();
} catch (IOException e) {
return die("getLatestVersion("+ r +"): "+shortError(e), e);
}
}

public String downloadHash(String roleName, String version) {
try {
return url2string(releaseUrlBase+"/"+ roleName +"/"+ version +"/"+ roleName +getSoftwareSuffix(roleName)+".sha256").trim();
} catch (IOException e) {
return die("getSoftwareHash("+ roleName +"): "+shortError(e), e);
}
}

@Getter(lazy=true) private final Properties defaultSoftwareVersions = initDefaultSoftwareVersions();
private Properties initDefaultSoftwareVersions() {
if (empty(releaseUrlBase)) {
log.warn("initDefaultSoftwareVersions: releaseUrlBase not defined");
return null;
}
final Properties props = new Properties();
if (!SOFTWARE_VERSIONS_FILE.exists()) {
// write latest versions
for (String roleName : VERSIONED_SOFTWARE) {
final String latestVersion = getLatestVersion(roleName);
props.setProperty(getRolePropBase(roleName)+SUFFIX_VERSION, latestVersion);
props.setProperty(getRolePropBase(roleName)+SUFFIX_SHA, downloadHash(roleName, latestVersion));
}
writeVersions(props, SOFTWARE_VERSIONS_FILE);
}
try (InputStream in = new FileInputStream(SOFTWARE_VERSIONS_FILE)) {
props.load(in);
return props;
} catch (Exception e) {
log.error("initDefaultSoftwareVersions: "+shortError(e));
return null;
}
}

public void writeVersions(File file) { writeVersions(getDefaultSoftwareVersions(), file); }

public void writeVersions(Properties props, File file) {
try (OutputStream out = new FileOutputStream(file)) {
props.store(out, null);
} catch (Exception e) {
log.error("saveSoftwareVersions: "+shortError(e));
}
}

public void writeAnsibleVars(File file) { writeAnsibleVars(getDefaultSoftwareVersions(), file); }

public void writeAnsibleVars(Properties props, File file) {
try (OutputStream out = new FileOutputStream(file)) {
final StringBuilder b = new StringBuilder();
for (String name : props.stringPropertyNames()) {
b.append(name).append(" : '").append(props.getProperty(name)).append("'\n");
}
FileUtil.toFile(file, b.toString());

} catch (Exception e) {
die("writeAnsibleVars: "+shortError(e));
}
}

private final Map<String, String> softwareVersions = new HashMap<>();

public String getSoftwareVersion(String roleName) {
final Properties defaults = getDefaultSoftwareVersions();
if (defaults != null) {
final String propName = getRolePropBase(roleName) + SUFFIX_VERSION;
final String version = defaults.getProperty(propName);
if (version != null) return version;
}
return softwareVersions.computeIfAbsent(roleName, this::getLatestVersion);
}

private final Map<String, String> softwareHashes = new HashMap<>();

public String getSoftwareHash(String roleName, String version) {
final Properties defaults = getDefaultSoftwareVersions();
if (defaults != null) {
final String roleBase = getRolePropBase(roleName);
final String foundVersion = defaults.getProperty(roleBase + SUFFIX_VERSION);
if (foundVersion != null && foundVersion.equals(version)) {
final String hash = defaults.getProperty(roleBase + SUFFIX_SHA);
if (hash != null) return hash;
}
}
return softwareHashes.computeIfAbsent(roleName, r -> downloadHash(r, version));
}

private String getSoftwareSuffix(String roleName) {
switch (roleName) {
case ROLE_ALGO: case ROLE_MITMPROXY: return ".zip";
case ROLE_DNSCRYPT: return "";
default: return die("getSoftwareSuffix: unrecognized roleName: "+roleName);
}
}

}

+ 3
- 5
bubble-server/src/main/java/bubble/service/boot/StandardSelfNodeService.java 파일 보기

@@ -441,14 +441,12 @@ public class StandardSelfNodeService implements SelfNodeService {
return planDAO.findByUuid(accountPlan.getPlan());
}

@Override
public boolean getLogFlag() {
@Override public boolean getLogFlag() {
var flagStr = getNodeConfig().get_plaintext(REDIS_LOG_FLAG_KEY);
return empty(flagStr) ? false : Boolean.valueOf(flagStr);
return Boolean.parseBoolean(flagStr);
}

@Override
@NonNull public Optional<Long> getLogFlagExpirationTime() {
@Override @NonNull public Optional<Long> getLogFlagExpirationTime() {
var ttl = getNodeConfig().get_ttl(REDIS_LOG_FLAG_KEY);
return ttl < 0 ? Optional.empty() : Optional.of(now() + ttl * 1000);
}


+ 1
- 1
bubble-server/src/main/java/bubble/service/cloud/AnsiblePrepService.java 파일 보기

@@ -161,7 +161,7 @@ public class AnsiblePrepService {
if (installType == AnsibleInstallType.sage) return (int) (((double) memoryMB) * 0.6d);
if (memoryMB >= 4096) return (int) (((double) memoryMB) * 0.6d);
if (memoryMB >= 2048) return (int) (((double) memoryMB) * 0.5d);
if (memoryMB >= 1024) return (int) (((double) memoryMB) * 0.196d);
if (memoryMB >= 1024) return (int) (((double) memoryMB) * 0.24d);
// no nodes are this small, API probably would not start, not enough memory
return (int) (((double) memoryMB) * 0.19d);
}


+ 14
- 8
bubble-server/src/main/java/bubble/service/dbfilter/DatabaseFilterService.java 파일 보기

@@ -42,6 +42,7 @@ import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.io.FileUtil.abs;
import static org.cobbzilla.wizard.server.config.PgRestServerConfiguration.ENV_PGPASSWORD;
import static org.cobbzilla.wizard.server.listener.FlywayMigrationListener.getFlywayTableName;

@Service @Slf4j
public class DatabaseFilterService {
@@ -150,14 +151,19 @@ public class DatabaseFilterService {
return die("copyDatabase: writer exited with an error (dbName="+dbName+"): "+writeResult.get());
}

// copy flyway schema table
log.debug("copyDatabase: dumping flyway_schema_version data");
final CommandResult flywayData = pgExec("pg_dump", dbConfig.getDatabaseName(), null, null, FLYWAY_DUMP_OPTIONS);
if (!flywayData.isZeroExitStatus()) return die("copyDatabase: error dumping flyway_schema_version data: "+flywayData);

log.debug("copyDatabase: inserting flyway_schema_version data");
final CommandResult flywayInsert = pgExec("psql", dbName, new ByteArrayInputStream(flywayData.getStdout().getBytes()), null);
if (!flywayInsert.isZeroExitStatus()) return die("copyDatabase: error inserting flyway_schema_version data: "+flywayInsert);
// copy flyway schema table, if it exists.
// it may not exist if this is the very first time the server has been run
if (configuration.tableExists(getFlywayTableName())) {
log.debug("copyDatabase: dumping flyway_schema_version data");
final CommandResult flywayData = pgExec("pg_dump", dbConfig.getDatabaseName(), null, null, FLYWAY_DUMP_OPTIONS);
if (!flywayData.isZeroExitStatus()) return die("copyDatabase: error dumping flyway_schema_version data: " + flywayData);

log.debug("copyDatabase: inserting flyway_schema_version data");
final CommandResult flywayInsert = pgExec("psql", dbName, new ByteArrayInputStream(flywayData.getStdout().getBytes()), null);
if (!flywayInsert.isZeroExitStatus()) return die("copyDatabase: error inserting flyway_schema_version data: "+flywayInsert);
} else {
log.warn("copyDatabase: flyway table ("+getFlywayTableName()+") does not exist, not copying");
}

// dump new DB
log.info("copyDatabase: dumping new database: "+dbName);


+ 17
- 3
bubble-server/src/main/java/bubble/service/packer/PackerJob.java 파일 보기

@@ -16,6 +16,7 @@ import bubble.model.account.Account;
import bubble.model.cloud.AnsibleInstallType;
import bubble.model.cloud.CloudService;
import bubble.server.BubbleConfiguration;
import bubble.server.SoftwareVersions;
import bubble.service.cloud.GeoService;
import lombok.Cleanup;
import lombok.Getter;
@@ -33,6 +34,7 @@ import org.cobbzilla.util.time.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
@@ -40,6 +42,7 @@ import java.util.stream.Collectors;

import static bubble.ApiConstants.copyScripts;
import static bubble.model.cloud.RegionalServiceDriver.findClosestRegions;
import static bubble.server.SoftwareVersions.*;
import static bubble.service.packer.PackerService.*;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.io.FileUtil.*;
@@ -61,12 +64,12 @@ public class PackerJob implements Callable<List<PackerImage>> {

public static final String INSTALL_TYPE_VAR = "@@TYPE@@";
public static final String SAGE_NET_VAR = "@@SAGE_NET@@";
public static final String PACKER_KEY_VAR = "@@PACKER_KEY_HASH@@";
public static final String PACKER_VERSION_HASH_VAR = "@@PACKER_VERSION_HASH@@";
public static final String BUBBLE_VERSION_VAR = "@@BUBBLE_VERSION@@";
public static final String TIMESTAMP_VAR = "@@TIMESTAMP@@";
public static final String PACKER_IMAGE_NAME_TEMPLATE = PACKER_IMAGE_PREFIX + INSTALL_TYPE_VAR
+ "_" + SAGE_NET_VAR
+ "_" + PACKER_KEY_VAR
+ "_" + PACKER_VERSION_HASH_VAR
+ "_" + BUBBLE_VERSION_VAR
+ "_" + TIMESTAMP_VAR;

@@ -157,6 +160,11 @@ public class PackerJob implements Callable<List<PackerImage>> {
// copy ansible and other packer files to temp dir
@Cleanup final TempDir tempDir = copyClasspathDirectory("packer");

// record versions of algo, mitmproxy and dnscrypt_proxy
writeSoftwareVars(ROLE_ALGO, tempDir);
writeSoftwareVars(ROLE_MITMPROXY, tempDir);
writeSoftwareVars(ROLE_BUBBLE, tempDir);

// copy packer ssh key
copyFile(packerService.getPackerPublicKey(), new File(abs(tempDir)+"/roles/common/files/"+PACKER_KEY_NAME));

@@ -212,7 +220,7 @@ public class PackerJob implements Callable<List<PackerImage>> {
final String imageName = PACKER_IMAGE_NAME_TEMPLATE
.replace(INSTALL_TYPE_VAR, installType.name())
.replace(SAGE_NET_VAR, truncate(domainname(), 19))
.replace(PACKER_KEY_VAR, packerService.getPackerPublicKeyHash())
.replace(PACKER_VERSION_HASH_VAR, packerService.getPackerVersionHash())
.replace(BUBBLE_VERSION_VAR, configuration.getShortVersion())
.replace(TIMESTAMP_VAR, TimeUtil.format(now(), DATE_FORMAT_YYYYMMDDHHMMSS));
if (imageName.length() > 128) return die("imageName.length > 128: "+imageName); // sanity check
@@ -282,6 +290,12 @@ public class PackerJob implements Callable<List<PackerImage>> {
return images;
}

private void writeSoftwareVars(String roleName, TempDir tempDir) throws IOException {
final SoftwareVersions softwareVersions = configuration.getSoftwareVersions();
final File varsDir = mkdirOrDie(abs(tempDir) + "/roles/"+roleName+"/vars");
softwareVersions.writeAnsibleVars(new File(varsDir, "main.yml"));
}

private List<String> getRolesForInstallType(AnsibleInstallType installType) {
switch (installType) {
case sage: return SAGE_ROLES;


+ 16
- 2
bubble-server/src/main/java/bubble/service/packer/PackerService.java 파일 보기

@@ -8,9 +8,9 @@ import bubble.cloud.compute.PackerImage;
import bubble.model.cloud.AnsibleInstallType;
import bubble.model.cloud.CloudService;
import bubble.server.BubbleConfiguration;
import bubble.server.SoftwareVersions;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.util.daemon.DaemonThreadFactory;
import org.cobbzilla.util.security.ShaUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@@ -22,10 +22,12 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static bubble.server.SoftwareVersions.*;
import static org.cobbzilla.util.daemon.ZillaRuntime.*;
import static org.cobbzilla.util.io.FileUtil.abs;
import static org.cobbzilla.util.io.FileUtil.mkdirOrDie;
import static org.cobbzilla.util.io.StreamUtil.stream2string;
import static org.cobbzilla.util.security.ShaUtil.sha256_file;
import static org.cobbzilla.util.string.StringUtil.splitAndTrim;
import static org.cobbzilla.util.system.CommandShell.chmod;
import static org.cobbzilla.util.system.CommandShell.execScript;
@@ -40,6 +42,7 @@ public class PackerService {

public static final List<String> NODE_ROLES = splitAndTrim(stream2string(PACKER_DIR + "/node-roles.txt"), "\n")
.stream().filter(s -> !empty(s)).collect(Collectors.toList());

public static final String PACKER_KEY_NAME = "packer_rsa";

private final Map<String, PackerJob> activeJobs = new ConcurrentHashMap<>(16);
@@ -82,7 +85,18 @@ public class PackerService {

public File getPackerPublicKey () { return initPackerKey(true); }
public File getPackerPrivateKey () { return initPackerKey(false); }
public String getPackerPublicKeyHash () { return ShaUtil.sha256_file(getPackerPublicKey()); }
public String getPackerPublicKeyHash () { return sha256_file(getPackerPublicKey()); }

public String getPackerVersionHash () {
final SoftwareVersions softwareVersions = configuration.getSoftwareVersions();
final String keyHash = getPackerPublicKeyHash();
final String versions = ""
+"_d"+softwareVersions.getSoftwareVersion(ROLE_DNSCRYPT)
+"_a"+softwareVersions.getSoftwareVersion(ROLE_ALGO)
+"_m"+softwareVersions.getSoftwareVersion(ROLE_MITMPROXY);
if (versions.length() > 48) return die("getPackerVersionHash: software versions are too long (versions.length == "+versions.length()+" > 48): "+versions);
return keyHash.substring(64 - versions.length())+versions;
}

public synchronized File initPackerKey(boolean pub) {
final File keyDir = new File(System.getProperty("user.home"),".ssh");


+ 2
- 0
bubble-server/src/main/java/bubble/service/stream/StandardAppPrimerService.java 파일 보기

@@ -214,6 +214,8 @@ public class StandardAppPrimerService implements AppPrimerService {
}
} catch (Exception e) {
die("_prime: "+shortError(e), e);
} finally {
log.info("_primeApps: completed");
}
}



+ 1
- 1
bubble-server/src/main/resources/META-INF/bubble/bubble.properties 파일 보기

@@ -1 +1 @@
bubble.version=Adventure 1.1.1
bubble.version=Adventure 1.1.3

+ 2
- 1
bubble-server/src/main/resources/ansible/bubble_scripts.txt 파일 보기

@@ -19,4 +19,5 @@ install_packer.sh
rkeys
rmembers
rdelkeys
mitm_pid
mitm_pid
reset_bubble_logs

+ 1
- 0
bubble-server/src/main/resources/ansible/roles/finalizer/files/bubble_role.json 파일 보기

@@ -6,6 +6,7 @@
{"name": "restore_key", "value": "[[restoreKey]]"},
{"name": "install_type", "value": "[[installType]]"},
{"name": "bubble_java_opts", "value": "-XX:MaxRAM=[[jvmMaxRamMB]]m"},
{"name": "total_memory", "value": "[[nodeSize.memoryMB]]"},
{"name": "cert_name", "value": "bubble-[[network.shortId]]"}
],
"optionalConfigNames": ["restore_key"]

+ 5
- 0
bubble-server/src/main/resources/ansible/roles/finalizer/tasks/main.yml 파일 보기

@@ -25,6 +25,11 @@
src: "supervisor_bubble.conf.j2"
dest: /etc/supervisor/conf.d/bubble.conf

# Save 1% of memory, every bit counts on small nodes
- name: Disable peer manager on small nodes
shell: bash -c "supervisorctl stop bubble_peer_manager && rm /etc/supervisor/conf.d/bubble_peer_manager.conf"
when: total_memory < 2048

- name: save iptables v4 rules
shell: iptables-save > /etc/iptables/rules.v4
become: yes


+ 1
- 0
bubble-server/src/main/resources/ansible/roles/mitmproxy/files/supervisor_mitm_monitor.conf 파일 보기

@@ -3,3 +3,4 @@
stdout_logfile = /dev/null
stderr_logfile = /dev/null
command=/usr/local/sbin/mitm_monitor.sh
stopsignal=QUIT

+ 1
- 0
bubble-server/src/main/resources/bubble-config.yml 파일 보기

@@ -76,6 +76,7 @@ localNotificationStrategy: {{#exists BUBBLE_LOCAL_NOTIFY}}{{BUBBLE_LOCAL_NOTIFY}

letsencryptEmail: {{LETSENCRYPT_EMAIL}}
localStorageDir: {{LOCALSTORAGE_BASE_DIR}}
releaseUrlBase: {{#exists BUBBLE_RELEASE_URL_BASE}}{{BUBBLE_RELEASE_URL_BASE}}{{else}}https://jenkins.bubblev.org/public/releases{{/exists}}

disallowedCountries: {{DISALLOWED_COUNTRIES}}



+ 2
- 2
bubble-server/src/main/resources/bubble/rule/RequestModifierRule_icon.js.hbs 파일 보기

@@ -87,10 +87,10 @@ function {{JS_PREFIX}}_create_button(labelKey, labelDefault, onclick, labelForma
return btn;
}

let {{PAGE_PREFIX}}_url_chasers = {};

if (typeof {{PAGE_PREFIX}}_icon_status === 'undefined') {

let {{PAGE_PREFIX}}_url_chasers = {};

{{PAGE_PREFIX}}_screenWidth = function () { return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth };

let {{PAGE_PREFIX}}_doc_ready = false;


+ 102
- 0
bubble-server/src/main/resources/bubble/rule/social/block/site/LI.js.hbs 파일 보기

@@ -0,0 +1,102 @@
{{JS_PREFIX}}_supports_keywords = true;

const {{JS_PREFIX}}_site_host = location.protocol + '//' + window.location.hostname + '/';

function {{JS_PREFIX}}_apply_blocks(blocked_users) {
const articles = Array.from(document.getElementsByClassName('feed-shared-update-v2'));
if (articles === null || articles.length === 0) {
console.warn('No articles found, not filtering');
return;
}
{{JS_PREFIX}}_consider_block(articles, blocked_users);
}

function {{JS_PREFIX}}_author_from_href(href) {
if (typeof href === 'undefined' || href === null) return null;
let h = href.startsWith({{JS_PREFIX}}_site_host) ? href.substring({{JS_PREFIX}}_site_host.length) : href;
const qPos = h.indexOf('?');
if (qPos !== -1) {
h = h.substring(0, qPos);
}
if (h.endsWith('/')) h = h.substring(0, h.length - 1);
if (!h.startsWith('in/') && !h.startsWith('company/')) {
return null;
}
const slashPos = h.indexOf('/');
const name = h.substring(slashPos);
if (name.length > 35 && name.indexOf('-') === -1 && name.indexOf('_') === -1) return null;
console.log("author_from_href: found "+name+' from '+href);
return name;
}

function {{JS_PREFIX}}_remove_article_from_dom(article) {
// todo: does this work?
article.parentNode.removeChild(article);
}

function {{JS_PREFIX}}_create_block_control(article, authorName, articleLink) {
let linkClass = articleLink.className;
if (linkClass && linkClass.indexOf('{{JS_PREFIX}}_link_decorated') !== -1) {
return null;
} else {
articleLink.className = articleLink.className ? articleLink.className + ' {{JS_PREFIX}}_link_decorated' : '{{JS_PREFIX}}_link_decorated';
}
const imgHolder = {{JS_PREFIX}}_create_block_img();
const blockSpan = document.createElement('span');
const blockLink = document.createElement('a');
blockLink.style.zIndex = '{{APP_CONTROLS_Z_INDEX}}'
blockLink.style.cursor = 'pointer';
blockLink.addEventListener("click", function (e) {
{{JS_PREFIX}}_remove_article_from_dom(article, authorName);
{{JS_PREFIX}}_block_user(authorName);
e.stopPropagation();
e.preventDefault();
return false;
});
blockLink.appendChild(imgHolder);
blockSpan.appendChild(document.createTextNode('\u00A0\u00A0'));
blockSpan.appendChild(blockLink);
blockSpan.id = 'blockSpan_'+{{JS_PREFIX}}_uuidv4();
console.log('adding block control on '+authorName);
return blockSpan;
}

function {{JS_PREFIX}}_consider_block(articles, blocked_users) {
if (articles && articles.length && articles.length > 0) {
for (let i=0; i<articles.length; i++) {
const article = articles[i];
const firstEval = {{JS_PREFIX}}_mark_evaluated(article);
if ({{JS_PREFIX}}_includes_block_keyword(article, firstEval)) {
{{JS_PREFIX}}_remove_article_from_dom(article);
continue;
}
const articleLinks = Array.from(article.getElementsByTagName('a'));
for (let j=0; j<articleLinks.length; j++) {
const articleLink = articleLinks[i];
console.log('consider_block: examining articleLink with href='+articleLink.href);
const author = {{JS_PREFIX}}_author_from_href(articleLink.href);
if (author === null) continue;
if (author in blocked_users) {
{{JS_PREFIX}}_tally_author_block(author);
if (!firstEval) {{JS_PREFIX}}_untally_allow();
{{JS_PREFIX}}_remove_article_from_dom(article);

} else if (firstEval) {
const authorSpans = Array.from(articleLink.getElementsByClassName('feed-shared-actor__name'));
if (authorSpans.length === 0) {
continue;
}

let b = {{JS_PREFIX}}_create_block_control(article, author, articleLink);
if (b !== null) {
console.log('consider_block: inserting span='+b.id+' for article by '+author);
authorSpans[0].parentNode.appendChild(b);
{{JS_PREFIX}}_tally_allow();
} else {
console.log('consider_block: create_block_control returned null for author '+author)
}
}
}
}
}
}

+ 23
- 0
bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li.json 파일 보기

@@ -0,0 +1,23 @@
[{
"name": "UserBlocker",
"children": {
"AppSite": [{
"name": "LinkedIn",
"url": "https://linkedin.com",
"description": "The world's largest professional network with 706+ million users in more than 200 countries and territories worldwide",
"template": true,
"maxSecurityHosts": [
"linkedin.com", "*.linkedin.com"
],
"enableMaxSecurityHosts": true
}],
"AppRule": [{
"name": "li_user_blocker",
"template": true,
"driver": "JsUserBlockerRuleDriver",
"config": {
"siteJsTemplate": "bubble/rule/social/block/site/LI.js.hbs"
}
}]
}
}]

+ 12
- 0
bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_data.json 파일 보기

@@ -0,0 +1,12 @@
[{
"name": "UserBlocker",
"children": {
"AppData": [{
"site": "LinkedIn",
"template": true,
"matcher": "LIMatcher",
"key": "kw:_<span>Promoted</span>",
"data": "true"
}]
}
}]

+ 15
- 0
bubble-server/src/main/resources/models/apps/user_block/li/bubbleApp_userBlock_li_matchers.json 파일 보기

@@ -0,0 +1,15 @@
[{
"name": "UserBlocker",
"children": {
"AppMatcher": [{
"name": "LIMatcher",
"site": "LinkedIn",
"template": true,
"requestCheck": true,
"requestModifier": true,
"fqdn": "www.linkedin.com",
"urlRegex": "/feed/",
"rule": "li_user_blocker"
}]
}
}]

+ 3
- 0
bubble-server/src/main/resources/models/manifest-app-user-block.json 파일 보기

@@ -15,6 +15,9 @@
"apps/user_block/insta/bubbleApp_userBlock_insta",
"apps/user_block/insta/bubbleApp_userBlock_insta_matchers",
"apps/user_block/insta/bubbleApp_userBlock_insta_data",
"apps/user_block/li/bubbleApp_userBlock_li",
"apps/user_block/li/bubbleApp_userBlock_li_matchers",
"apps/user_block/li/bubbleApp_userBlock_li_data",
"apps/user_block/reddit/bubbleApp_userBlock_reddit",
"apps/user_block/reddit/bubbleApp_userBlock_reddit_matchers"
]

+ 2
- 2
bubble-server/src/main/resources/packer/roles/algo/tasks/main.yml 파일 보기

@@ -11,9 +11,9 @@

- name: Download algo dist file
get_url:
url: https://github.com/getbubblenow/bubble-dist/raw/master/algo/master.zip
url: https://jenkins.bubblev.org/public/releases/algo/{{ algo_version }}/algo.zip
dest: /tmp/algo.zip
checksum: sha256:0c32e213ff4cfd807718cb276f6160d4b587779c94fba2c2102905b7303720f8
checksum: sha256:{{ algo_sha }}

- name: Unzip algo master.zip
unarchive:


+ 7
- 1
bubble-server/src/main/resources/packer/roles/bubble/files/refresh_bubble_ssh_keys.sh 파일 보기

@@ -28,6 +28,7 @@ NEW_KEYS=$(mktemp /root/.ssh/authorized_keys.XXXXXXX)
chmod 600 ${NEW_KEYS} || die "Error setting permissions on new authorized_keys file: ${NEW_KEYS}"

KEY_COUNT=0
ALL_KEY_COUNT=0
for key in $(echo "${CURRENT_KEYS_SQL}" | PGPASSWORD="$(cat /home/bubble/.BUBBLE_PG_PASSWORD)" psql -U bubble -h 127.0.0.1 bubble -qt) ; do
if [[ -z "$(echo "${key}" | tr -d [[:space:]])" ]] ; then
continue
@@ -40,14 +41,19 @@ for key in $(echo "${CURRENT_KEYS_SQL}" | PGPASSWORD="$(cat /home/bubble/.BUBBLE
else
log "Warning: NOT adding malformed key: $(echo "${KEY}" | tr -d '\n')"
fi
ALL_KEY_COUNT=$(expr ${ALL_KEY_COUNT} + 1)
done

if [[ ${KEY_COUNT} -eq 0 ]] ; then
# Sanity check that we can even talk to psql
# We may be out of memory, in which case we do not want to erase existing installed keys
if [[ -z "$(echo 'SELECT count(*) FROM account_ssh_key' | PGPASSWORD="$(cat /home/bubble/.BUBBLE_PG_PASSWORD)" psql -U bubble -h 127.0.0.1 bubble)" ]] ; then
CHECK_KEY_COUNT="$(echo 'SELECT count(*) FROM account_ssh_key' | PGPASSWORD="$(cat /home/bubble/.BUBBLE_PG_PASSWORD)" psql -U bubble -h 127.0.0.1 bubble)"
if [[ -z "${CHECK_KEY_COUNT}" ]] ; then
log "Warning: error calling psql, not installing/uninstalling any keys"
exit 0
elif [[ ${CHECK_KEY_COUNT} -ne ${ALL_KEY_COUNT} ]] ; then
log "Warning: error CHECK_KEY_COUNT (${CHECK_KEY_COUNT}) -ne ALL_KEY_COUNT (${ALL_KEY_COUNT}), not installing/uninstalling any keys"
exit 0
fi
fi



+ 8
- 0
bubble-server/src/main/resources/packer/roles/bubble/tasks/main.yml 파일 보기

@@ -132,3 +132,11 @@
special_time: "hourly"
user: "root"
job: "find /tmp ~bubble/tmp -mtime +1 -type f -delete && find /tmp ~bubble/tmp -mtime +1 -type d -empty -delete"

- name: Record software versions
template:
src: bubble_versions.properties.j2
dest: /home/bubble/bubble_versions.properties
owner: bubble
group: bubble
mode: 0400

+ 6
- 0
bubble-server/src/main/resources/packer/roles/bubble/templates/bubble_versions.properties.j2 파일 보기

@@ -0,0 +1,6 @@
algo_version={{ algo_version }}
algo_sha={{ algo_sha }}
dnscrypt_proxy_version={{ dnscrypt_proxy_version }}
dnscrypt_proxy_sha={{ dnscrypt_proxy_sha }}
mitmproxy_version={{ mitmproxy_version }}
mitmproxy_sha={{ mitmproxy_sha }}

+ 37
- 0
bubble-server/src/main/resources/packer/roles/mitmproxy/files/_client.py 파일 보기

@@ -1,3 +1,36 @@
#
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
#
# Adapted from _client.py in the httpx project. The httpx license is reprinted here:
#
# Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import functools
import typing
from types import TracebackType
@@ -1348,6 +1381,10 @@ class AsyncClient(BaseClient):

if allow_redirects:
await response.aread()
# THIS IS THE ONLY THING THAT WAS CHANGED
# The line below is indented here, was not in original file
# When allow_redirects is False, we do not need to build a redirect request, which fails
# because when the request is streamed, we can't read it again here
request = self._build_redirect_request(request, response)
history = history + [response]



+ 30
- 0
bubble-server/src/main/resources/packer/roles/mitmproxy/files/_events.py 파일 보기

@@ -1,3 +1,31 @@
#
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
#
# Adapted from _events.py in the h11 project. The h11 license is reprinted here:
#
# The MIT License (MIT)
#
# Copyright (c) 2016 Nathaniel J. Smith <njs@pobox.com> and other contributors
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# High level events that make up HTTP/1.1 conversations. Loosely inspired by
# the corresponding events in hyper-h2:
#
@@ -220,6 +248,8 @@ class Response(_ResponseBase):
"""

def _validate(self):
# THIS IS THE ONLY THING THAT WAS CHANGED
# Certain websites return HTTP status 999 to mean 200
if self.status_code == 999:
self.status_code = 200
if not (200 <= self.status_code < 600):


+ 2
- 2
bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_api.py 파일 보기

@@ -436,8 +436,8 @@ def is_flex_domain(client_addr, server_addr, fqdns):
bubble_log.debug('is_flex_domain: returning True for: '+fqdn+' (check='+check_fqdn+')')
return True
check_fqdn = check_fqdn[check_fqdn.index('.')+1:]
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('is_flex_domain: (early) returning False for: '+fqdn)
# if bubble_log.isEnabledFor(DEBUG):
# bubble_log.debug('is_flex_domain: returning False for: '+fqdn)
return False




+ 6
- 8
bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_modify.py 파일 보기

@@ -2,17 +2,16 @@
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
#
import asyncio
import base64
import json
import re
import urllib
import traceback

from mitmproxy.net.http import Headers
from mitmproxy.proxy.protocol.async_stream_body import AsyncStreamBody

from bubble_config import bubble_port, debug_capture_fqdn, debug_stream_fqdn, debug_stream_uri
from bubble_api import CTX_BUBBLE_MATCHERS, CTX_BUBBLE_ABORT, CTX_BUBBLE_LOCATION, \
CTX_BUBBLE_FLEX, CTX_BUBBLE_SPECIAL, \
from bubble_api import CTX_BUBBLE_MATCHERS, CTX_BUBBLE_ABORT, CTX_BUBBLE_LOCATION, CTX_BUBBLE_FLEX, \
status_reason, get_flow_ctx, add_flow_ctx, bubble_async, async_client, cleanup_async, \
is_bubble_special_path, is_bubble_health_check, health_check_response, special_bubble_response, \
CTX_BUBBLE_REQUEST_ID, CTX_CONTENT_LENGTH, CTX_CONTENT_LENGTH_SENT, CTX_BUBBLE_FILTERED, \
@@ -64,10 +63,10 @@ def ensure_bubble_csp(csp, req_id):
break
# if no nonce, then add our nonce
if not found_nonce:
new_csp = add_csp_part(new_csp, " ".join(tokens) + " 'nonce="+req_id+"'")
new_csp = add_csp_part(new_csp, " ".join(tokens) + " 'nonce-"+base64.b64encode(bytes(req_id, 'utf-8')).decode()+"' ")
else:
# does not allow from self, so add self with our nonce
new_csp = add_csp_part(new_csp, tokens[0] + " 'self' 'nonce="+req_id+"'" + " ".join(tokens[1:]))
new_csp = add_csp_part(new_csp, tokens[0] + " 'self' 'nonce-"+base64.b64encode(bytes(req_id, 'utf-8')).decode()+"' " + " ".join(tokens[1:]))
else:
new_csp = add_csp_part(new_csp, part)
return new_csp
@@ -285,8 +284,6 @@ def responseheaders(flow):
flex_flow = process_flex(flex_flow)
else:
flex_flow = None
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('responseheaders: flex_flow = '+repr(flex_flow))
bubble_filter_response(flow, flex_flow)


@@ -302,7 +299,8 @@ def bubble_filter_response(flow, flex_flow):
if is_bubble_health_check(path):
health_check_response(flow)
else:
bubble_log.info('bubble_filter_response: sending special bubble response for path: '+path)
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('bubble_filter_response: sending special bubble response for path: '+path)
special_bubble_response(flow)

elif flex_flow and flex_flow.is_error():


+ 19
- 20
bubble-server/src/main/resources/packer/roles/mitmproxy/files/bubble_request.py 파일 보기

@@ -30,9 +30,9 @@ import uuid
from mitmproxy.net.http import headers as nheaders

from bubble_api import bubble_matchers, bubble_activity_log, \
HEALTH_CHECK_URI, CTX_BUBBLE_MATCHERS, CTX_BUBBLE_SPECIAL, CTX_BUBBLE_ABORT, CTX_BUBBLE_LOCATION, \
CTX_BUBBLE_MATCHERS, CTX_BUBBLE_SPECIAL, CTX_BUBBLE_ABORT, CTX_BUBBLE_LOCATION, \
CTX_BUBBLE_PASSTHRU, CTX_BUBBLE_FLEX, CTX_BUBBLE_REQUEST_ID, add_flow_ctx, parse_host_header, \
is_bubble_special_path, is_bubble_health_check, \
is_bubble_special_path, is_bubble_health_check, health_check_response, \
is_bubble_request, is_sage_request, is_not_from_vpn, is_flex_domain
from bubble_config import bubble_host, bubble_host_alias
from bubble_flex import new_flex_flow
@@ -49,11 +49,9 @@ class Rerouter:
if host is None:
return None

is_health_check = is_bubble_health_check(flow.request.path)
if is_bubble_special_path(flow.request.path):
if not is_health_check:
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug("get_matchers: not filtering special bubble path: "+flow.request.path)
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug("get_matchers: not filtering special bubble path: "+flow.request.path)
return None

client_addr = str(flow.client_conn.address[0])
@@ -138,7 +136,6 @@ class Rerouter:
port = int(m.group("port"))

# Determine if this request should be filtered
is_health_check = False
host = None
path = flow.request.path
if sni or host_header:
@@ -151,14 +148,17 @@ class Rerouter:
# If http, we validate client/server here
if is_http:
fqdns = [host]
if is_bubble_request(server_addr, fqdns):
is_health_check = path.startswith(HEALTH_CHECK_URI)
if not is_health_check:
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('bubble_handle_request: redirecting to https for LOCAL bubble=' + server_addr +' (bubble_host (' + bubble_host +') in fqdns or bubble_host_alias (' + bubble_host_alias +') in fqdns) for client=' + client_addr +', fqdns=' + repr(fqdns) +', path=' + path)
add_flow_ctx(flow, CTX_BUBBLE_ABORT, 301)
add_flow_ctx(flow, CTX_BUBBLE_LOCATION, 'https://' + host + path)
return None
if is_bubble_health_check(path):
# Health check
health_check_response(flow)
return None

elif is_bubble_request(server_addr, fqdns):
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('bubble_handle_request: redirecting to https for LOCAL bubble=' + server_addr +' (bubble_host (' + bubble_host +') in fqdns or bubble_host_alias (' + bubble_host_alias +') in fqdns) for client=' + client_addr +', fqdns=' + repr(fqdns) +', path=' + path)
add_flow_ctx(flow, CTX_BUBBLE_ABORT, 301)
add_flow_ctx(flow, CTX_BUBBLE_LOCATION, 'https://' + host + path)
return None

elif is_sage_request(server_addr, fqdns):
if bubble_log.isEnabledFor(DEBUG):
@@ -170,7 +170,7 @@ class Rerouter:
elif is_not_from_vpn(client_addr):
# todo: add to fail2ban
if bubble_log.isEnabledFor(WARNING):
bubble_log.warning('bubble_handle_request: returning 404 for non-VPN client='+client_addr+', url='+log_url)
bubble_log.warning('bubble_handle_request: returning 404 for non-VPN client='+client_addr+', url='+log_url+' host='+host)
bubble_activity_log(client_addr, server_addr, 'http_abort_non_vpn', fqdns)
add_flow_ctx(flow, CTX_BUBBLE_ABORT, 404)
return None
@@ -225,10 +225,9 @@ class Rerouter:
bubble_log.debug('bubble_handle_request: no rules returned, passing thru...')
bubble_activity_log(client_addr, server_addr, 'http_no_rules', log_url)
else:
if not is_health_check:
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('bubble_handle_request: no matcher_response returned, passing thru...')
# bubble_activity_log(client_addr, server_addr, 'http_no_matcher_response', log_url)
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('bubble_handle_request: no matcher_response returned, passing thru...')
# bubble_activity_log(client_addr, server_addr, 'http_no_matcher_response', log_url)

elif is_http and is_not_from_vpn(client_addr):
# todo: add to fail2ban


+ 14
- 7
bubble-server/src/main/resources/packer/roles/mitmproxy/files/mitm_monitor.sh 파일 보기

@@ -3,6 +3,10 @@
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
#
LOG=/var/log/bubble/mitm_monitor.log
if [[ ! -f ${LOG} ]] ; then
touch ${LOG}
fi
chgrp mitmproxy ${LOG} && chmod 660 ${LOG} # allow run_mitm.sh to write to this log

function die {
echo 1>&2 "${1}"
@@ -76,22 +80,25 @@ function fullMitmReset {
function healthCheck {
MITM_PORT=${1}
START=$(date +%s)
HEALTH_CHECK_TIMEOUT=20
HEALTH_CHECK_TIMEOUT=30
HEALTH_OK="NOT_RUN"
HC_URL="http://$(hostname):${MITM_PORT}/__bubble/__mitm_health"
while [[ $(expr $(date +%s) - ${START}) -le ${HEALTH_CHECK_TIMEOUT} ]] ; do
# log "Performing health check on mitm${MITM_PORT}..."
CURL_OUT="$(curl --silent --connect-timeout 2 --max-time 2 http://$(hostname):${MITM_PORT}/__bubble/__mitm_health 2>> ${LOG})"
# log "Performing health check on mitm${MITM_PORT} via ${HC_URL} ..."
CURL_OUT="$(curl --silent --connect-timeout 2 --max-time 2 ${HC_URL} 2>> ${LOG})"
if [[ ! -z ${CURL_OUT} && ${CURL_OUT} == "OK" ]] ; then
# log "Health check on mitm${MITM_PORT}: OK"
# log "Health check on mitm${MITM_PORT} via ${HC_URL} : OK"
echo -n "OK"
return
else
log "Health check on mitm${MITM_PORT}: failed: ${CURL_OUT}"
log "Health check on mitm${MITM_PORT} via ${HC_URL} failed: ${CURL_OUT}"
HEALTH_OK="CURL_FAIL"
fi
sleep 1s
sleep 5s
DELTA=$(expr $(date +%s) - ${START})
log "$(expr ${HEALTH_CHECK_TIMEOUT} - ${DELTA}) seconds before restart"
done
log "Health check: final failure for mitm${MITM_PORT}, returning ${HEALTH_OK}"
log "Health check: final failure for mitm${MITM_PORT} via ${HC_URL} returning ${HEALTH_OK}"
echo -n "${HEALTH_OK}"
}



+ 30
- 2
bubble-server/src/main/resources/packer/roles/mitmproxy/files/run_mitm.sh 파일 보기

@@ -3,12 +3,40 @@
# Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
#
PORT=${1:-8888}
echo "Starting mitmproxy on port ${PORT} ..."
MITM_PORT_FILE=/home/mitmproxy/mitmproxy_port
LOG=/var/log/bubble/mitm_monitor.log

function log {
echo "[mitm${PORT}] $(date): ${1}" | tee -a ${LOG}
}

if [[ -f ${MITM_PORT_FILE} ]] ; then
MITM_PORT="$(cat ${MITM_PORT_FILE})"
START=$(date +%s)
TIMEOUT=30
while [[ ! -s "${MITM_PORT_FILE}" ]] ; do
log "MITM_PORT_FILE was empty: ${MITM_PORT_FILE} -- waiting for it to exist"
if [[ $(expr $(date +%s) - ${START}) -gt ${TIMEOUT} ]] ; then
log "timeout waiting for MITM_PORT_FILE to exist: ${MITM_PORT_FILE} -- starting anyway"
break
fi
sleep 5s
done
if [[ -s ${MITM_PORT_FILE} ]] ; then
MITM_PORT="$(cat ${MITM_PORT_FILE})"
if [[ ! -z "${MITM_PORT}" && ${MITM_PORT} -ne ${PORT} ]] ; then
log "Our port (${PORT}) is not the primary mitm port (${MITM_PORT}), delaying startup by 30 seconds"
sleep 30s
fi
fi
fi

log "Starting mitmproxy on port ${PORT} ..."

VENV_DIR="/home/mitmproxy/mitmproxy/venv"
SETUP_VENV=1
if [[ -d ${VENV_DIR} && $(find ${VENV_DIR} -type f -name "redis*" | wc -c | tr -d ' ') -gt 0 ]] ; then
echo "venv dir looks OK, skipping venv setup"
log "venv dir looks OK, skipping venv setup"
SETUP_VENV=0
fi



+ 4
- 4
bubble-server/src/main/resources/packer/roles/mitmproxy/tasks/main.yml 파일 보기

@@ -39,9 +39,9 @@

- name: Download mitmproxy dist file
get_url:
url: https://github.com/getbubblenow/bubble-dist/raw/master/mitmproxy/mitmproxy.zip
url: https://jenkins.bubblev.org/public/releases/mitmproxy/{{ mitmproxy_version }}/mitmproxy.zip
dest: /tmp/mitmproxy.zip
checksum: sha256:eebde85f9d49f34634695c3e3916f5940a31e5393d52d3f2df793a2a4f9f6430
checksum: sha256:{{ mitmproxy_sha }}

- name: Unzip mitmproxy.zip
unarchive:
@@ -93,14 +93,14 @@
shell: su - mitmproxy -c "bash -c 'cd /home/mitmproxy/mitmproxy && ./dev.sh'"

- name: Patch _client.py from httpx to fix bug with HTTP/2 redirects
file:
copy:
src: _client.py
dest: /home/mitmproxy/mitmproxy/venv/lib/python3.8/site-packages/httpx/_client.py
owner: mitmproxy
group: mitmproxy

- name: Patch _events.py from h11 to fix bug with HTTP status 999 being considered invalid
file:
copy:
src: _events.py
dest: /home/mitmproxy/mitmproxy/venv/lib/python3.8/site-packages/h11/_events.py
owner: mitmproxy


+ 1
- 1
bubble-web

@@ -1 +1 @@
Subproject commit 3a0cbf5aee69d9a88b3a3b42a658038e9c3d4016
Subproject commit 84b746a3751a68c71b3d60e6a3a30d08e93b8b82

+ 1
- 1
utils/cobbzilla-utils

@@ -1 +1 @@
Subproject commit c66f9e167ca32887374ec07a05fd3bddaeb8c2d5
Subproject commit ea72ac4a1619c4f5915047650cdd18b8a6202681

+ 1
- 1
utils/cobbzilla-wizard

@@ -1 +1 @@
Subproject commit 7716949a32c967ae62989d52745146b9cfcb8698
Subproject commit cd9cf18c90a373f024163c048d57cf574ec97293

불러오는 중...
취소
저장