Merge branch 'simonmicro:master' into patch-1

This commit is contained in:
Amir Moradi
2023-05-21 21:42:36 +02:00
committed by GitHub
14 changed files with 3951 additions and 203 deletions

View File

@@ -26,12 +26,11 @@ Make sure to support the developers by buying the choosen subscription for your
This is _optional_. You can simply use the default instance of this API (host is noted inside the `setup.py` script) and profit from "automatic" updates.
Just transfer the `www` files inside a public accessible root-folder on your _dedicated_ Apache webserver (really everthing with PHP support works). Also make sure your instance has a valid SSL-certificate (Let's encrypt is enough), otherwise it may won't work.
An example Apache install process can be found [here](docs/apache/install.md). If you want to test your instance, just open the public accessible URI in your browser and append `/notification` to it - if you see some JSON with the text, then everything worked!
An example Apache install process can be found [here](docs/apache/install.md). If you want to test your instance, just open the public accessible URI in your browser and append `/healthz` to it - if you see some JSON with the text, then everything worked!
### Nett2Know ###
* This modification will also block any communication to the Pritunl servers - so no calling home :)
* The `ultimate` mode is still a little bit buggy. This is caused by some hacky workarounds to get all features displayed (the server is already unlocked). Caused by this workaround some items are maybe shown instead of being hidden. If you find such thing - just ping me about it.
* SSO will not work with this api version! As Pritunls own authentication servers handle the whole SSO stuff, track instance ids and verify users I won't implement this part for privacy concerns (and also this would need to be securly implemented and need a database).
* SSO will not work with this api version! As Pritunls own authentication servers handle the whole SSO stuff, track instance ids and verify users, I won't implement this part for privacy concerns (and also this would need to be securly implemented and a database).
* This api has also its own docker image. Take a look into the `docker` folder and enjoy!
Have fun with your new premium/enterprise/ultimate Pritunl instance!

1
docker/.dockerignore Normal file
View File

@@ -0,0 +1 @@
mongodb

1
docker/.gitignore vendored
View File

@@ -1 +1,2 @@
setup.py
mongodb

View File

@@ -3,6 +3,4 @@ FROM goofball222/pritunl:latest
# Yes, you will need to copy it over into the build context...
COPY setup.py .
RUN chmod +x setup.py
RUN python3 -u setup.py --install
#RUN rm setup.py
RUN chmod +x setup.py; python3 -u setup.py --install; rm setup.py

View File

@@ -7,9 +7,10 @@ services:
- ./mongodb:/data/db
pritunl:
# Use the following to build the image from source.
# Use the following to build the image from source (assuming you're running inside the repository).
build:
context: .
context: ../server
dockerfile: ../docker/Dockerfile
restart: always
depends_on:
- mongodb

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d43025688bf8e6ddfa1234615e538005af6032c4fe5c3d72b6902eefaa035173
size 5095831

View File

@@ -1,4 +1,4 @@
#!/usr/lib/python3
#!/usr/bin/env python3
import os
import glob
import time

50
www/custom.css Normal file
View File

@@ -0,0 +1,50 @@
* {
color: rgb(57, 83, 120);
}
.dark * {
color: rgb(220, 232, 232);
}
.navbar .navbar-brand {
animation-name: pritunl-logo;
animation-duration: 20s;
animation-iteration-count: infinite;
}
@keyframes pritunl-logo {
0% {
transform: rotate3d(1, 0, 0, 360deg);
}
25% {
transform: rotate3d(1, 0, 0, 0deg);
}
50% {
transform: rotate3d(0, 1, 0, 0deg);
}
75% {
transform: rotate3d(0, 1, 0, 360deg);
}
100% {
transform: rotate3d(0, 1, 0, 360deg);
}
}
body::before {
position: fixed;
bottom: 0;
right: 0;
content: '';
background: url("BACKGROUND_IMAGE_URI");
background-size: contain;
background-repeat: no-repeat;
width: 10em;
height: 10em;
margin: 1em;
opacity: 0.1;
z-index: -99;
}

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +0,0 @@
/* Enable the display of the uids */
.users-list .users-list-title .org-id, .users-list .user .name-container .user-id, .servers-list .server .server-title .server-id, .servers-list .server .org .org-id, .servers-list .server .host .host-id, .servers-list .server .link .link-id {
display: inline-block;
}

3617
www/enterprise_plus.css Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,123 +1,164 @@
<?php
//Author: simonmicro 2022
// Author: simonmicro 2023
header("Access-Control-Allow-Origin: *"); //Allow access from everywhere...
$code = 200;
// Config
$minVersionNumber = 1003235044068;
$minVersionName = '1.32.3504.68';
$licenseCosts = 42; // insert here any price you want - "0" is a special value, which also breaks the UI ;)
header('Access-Control-Allow-Origin: *'); //Allow access from everywhere...
$code = 200; // Assuming everything is fine for now
// Parse body (if possible)
$body = json_decode(file_get_contents('php://input'));
$clientVersion = isset($body->version) ? $body->version : null;
// Fake API
$result = null;
if(isset($_GET['path'])) {
$path = trim($_GET['path'], ' /');
$pathParts = explode('/', $_GET['path']);
if(count($pathParts) > 0 && $pathParts[0] == 'healthz') {
$result = 'OK';
} else if(count($pathParts) > 1 && $pathParts[0] == 'notification') {
// Any notification/[version] will be answered here
if(preg_match('/notification.*/', $_GET['path'])) {
$result = new stdClass;
$result->message = 'Fake API endpoint for v1.30.3116.68 active and reachable (contacted at ' . date('r') . ').';
$result->vpn = false; //Idk
$result->www = false; //Idk
} else if(isset($body->license) && preg_match('/subscription.*/', $_GET['path'])) {
//The following only works with the body containing the desired license
$result = new stdClass;
$license = null;
//The stylesheet determines what is shown on the dashboard (and by the plan). As default we change the colors of any text.
$stylesheet = '';
if(preg_match('/.*premium/', $body->license)) {
$license = 'premium';
} else if(preg_match('/.*enterprise/', $body->license)) {
$license = 'enterprise';
$stylesheet .= file_get_contents('enterprise.css');
//Now fix some too aggressive display strategies by appending their overrides...
$stylesheet .= file_get_contents('enterprise_fix.css');
} else if(preg_match('/.*ultimate/', $body->license)) {
$license = 'enterprise_plus';
//Load the new css file and change all invisible blocks to visible (this will show a little bit too much, but whatever...)
$stylesheet .= file_get_contents('enterprise.css');
$stylesheet = preg_replace('/(enterprise)/', '$1-temp-prefix', $stylesheet);
$stylesheet = preg_replace('/(enterprise)(-temp-prefix-plus)/', '$1', $stylesheet);
$stylesheet = preg_replace('/(enterprise)(-temp-prefix)/', '$1-plus', $stylesheet);
$stylesheet = preg_replace('/(display:.?)none.?$/m', '$1inline-block', $stylesheet); //This WILL SHOW TOO MUCH... So we'll need a fix file...
$stylesheet .= file_get_contents('ultimate_fix.css');
$msg = 'Fake API endpoint for v' . $minVersionName . ' active and reachable (contacted at ' . date('r') . ').';
if(intval($pathParts[1]) < $minVersionNumber) {
$msg .= ' Please update your Pritunl instance to a newer version as this endpoint may not compatible anymore.';
}
$stylesheet .= "* { color: rgb(57, 83, 120); }\n.dark * { color: rgb(200, 242, 242); }\n.navbar .navbar-brand { animation-name: pritunl-logo; animation-duration: 20s; animation-iteration-count: infinite; }\n@keyframes pritunl-logo { 0% { transform:rotate3d(1, 0, 0, 360deg); } 25% { transform:rotate3d(1, 0, 0, 0deg); } 50% { transform:rotate3d(0, 1, 0, 0deg); } 75% { transform:rotate3d(0, 1, 0, 360deg); } 100% { transform:rotate3d(0, 1, 0, 360deg); } }\n.footer-brand {visibility: hidden; }\n.footer-brand::before { visibility: visible; position: absolute; bottom: 0; right: 0; content: ''; background: url('https://" . $_SERVER['HTTP_HOST'] . "/logo.png'); background-size: cover; width: 1em; height: 1em; margin: 0.3em; }\n/* Generated for $license license */";
$result = array(
'message' => $msg,
'vpn' => false, // idk
'www' => false // idk
);
} else if(count($pathParts) > 0 && $pathParts[0] == 'auth') {
$result = array('error_msg' => 'Sorry, but SSO is currently not supported.');
$code = 401; // Let Pritunl fail, without 500 codes (it will show 405)
} else if(count($pathParts) > 0 && $pathParts[0] == 'ykwyhd') {
// The "you-know-what-you-have-done" endpoint -> used as dummy url target
$result = array('detail' => 'You know what you have done.');
} else if($clientVersion != null && $clientVersion < $minVersionNumber) {
// Check if the instance is too old for us (for now following operators)
$result = array('error_msg' => 'This API supports v' . $minVersionName . ' (' . $minVersionNumber . ') or higher.');
$code = 473;
} else if(count($pathParts) > 0 && $pathParts[0] == 'subscription') {
// The following only works with the body containing the desired license
if(isset($body->license)) {
$license = null;
$user = md5(base64_encode($body->license));
$url_key = substr($user, 0, 8);
$input = strtolower($body->license);
# Workaround for 70b354a10df55d60515f76d851dee42939864395
if($body->version >= 1003031084050 and $body->version < 1003031164068)
$stylesheet = base64_encode($stylesheet);
// The stylesheet determines what is shown on the dashboard (and by the plan).
$stylesheet = '';
if(str_contains($input, 'premium')) {
$license = 'premium';
$stylesheet = file_get_contents('premium.css');
// No need to install the user license "id" into CSS class, as that file only contains custom patches
} else if(str_contains($input, 'enterprise')) {
$license = 'enterprise';
$stylesheet = file_get_contents('enterprise.css');
$stylesheet = preg_replace('/(\.enterprise)([\.\ ])/', '$1-'.$url_key.'$2', $stylesheet); // Install user license "id" into CSS class
} else if(str_contains($input, 'ultimate')) {
$license = 'enterprise_plus';
$stylesheet = file_get_contents('enterprise_plus.css');
$stylesheet = preg_replace('/(\.enterprise-plus)([\.\ ])/', '$1-'.$url_key.'$2', $stylesheet); // Install user license "id" into CSS class
}
$stylesheet .= "\n/* custom.css */\n";
$stylesheet .= str_replace('BACKGROUND_IMAGE_URI', "https://" . $_SERVER['HTTP_HOST'] . "/logo.png", file_get_contents('custom.css'));
$stylesheet .= "\n/* Generated for $license license */";
$state = null;
if($license) { // The following only makes sense if you selected any license
if(strpos($body->license, 'bad') !== false) {
if(str_starts_with($input, 'bad')) {
$state = 'Bad';
} else if(strpos($body->license, 'canceled') !== false) {
} else if(str_starts_with($input, 'canceled')) {
$state = 'canceled';
} else if(strpos($body->license, 'active') !== false) {
} else if(str_starts_with($input, 'active')) {
$state = 'Active';
}
}
if($state == 'Active') {
$result->active = $body->version < 1003031164068 ? $license != 'premium' : $license == 'enterprise_plus';
$result->status = $state;
$result->plan = $license;
$result->quantity = 42;
$result->amount = 42;
$result->credit = 42;
$result->period_end = false;
$result->trial_end = false;
$result->cancel_at_period_end = false;
$result->styles = new stdClass;
$result->styles->etag = 42;
$result->styles->last_modified = time();
$result->styles->data = $stylesheet;
}
if($state == 'Canceled') {
$result->active = false; //Here we can savely disable any styles
$result->status = $state;
$result->plan = $license;
$result->quantity = 42;
$result->amount = 42;
$result->period_end = false;
$result->trial_end = false;
$result->cancel_at_period_end = false;
$result->styles = new stdClass;
$result->styles->etag = 42;
$result->styles->last_modified = time();
$result->styles->data = $stylesheet;
}
if($state == 'Bad' || $state == null) {
$result = array(
'active' => true, // if the sub is not active, the css won't use the LICENSE-subscription_id pattern
'status' => $state,
'plan' => $license,
'url_key' => $user,
'quantity' => 42,
'amount' => $licenseCosts,
'credit' => 42,
'period_end' => false,
'trial_end' => false,
'cancel_at_period_end' => false,
'premium_buy_url' => 'https://' . $_SERVER['HTTP_HOST'] . '/ykwyhd/',
'enterprise_buy_url' => 'https://' . $_SERVER['HTTP_HOST'] . '/ykwyhd/',
'portal_url' => 'https://' . $_SERVER['HTTP_HOST'] . '/ykwyhd/',
'styles' => array(
'etag' => null, // the resource is NOT encrypted
'last_modified' => time(),
'data' => $stylesheet
)
);
} else if($state == 'Canceled') {
$result = array(
'active' => false, // Here we can savely disable any style
'status' => $state,
'plan' => $license,
'quantity' => 42,
'amount' => 42,
'period_end' => false,
'trial_end' => false,
'cancel_at_period_end' => false,
'styles' => array(
'etag' => null,
'last_modified' => null,
'data' => null
)
);
} else if($state == 'Bad' || $state == null) {
$code = 470; // -> bad license
$result->error_msg = 'As you wish.';
$result->error = 'license_invalid';
$result->active = false;
$result->status = false;
$result->plan = null;
$result->quantity = 0;
$result->amount = 0;
$result->period_end = true;
$result->trial_end = true;
$result->cancel_at_period_end = null;
$result->styles = new stdClass;
// Do not mention "canceled" in "error_msg", as it is somewhat useless (same as bad)...
$result = array(
'error' => 'license_invalid',
'error_msg' => $state == null ? 'Unknown command. Use ["bad" | "active"] ["premium" | "enterprise" | "ultimate"].' : 'As you wish.',
'active' => false,
'status' => null,
'plan' => null,
'quantity' => null,
'amount' => null,
'period_end' => null,
'trial_end' => null,
'cancel_at_period_end' => null,
'styles' => array(
'etag' => null,
'last_modified' => null,
'data' => null
)
);
}
if($state == null) {
$result->error_msg = 'Unknown command. Use ["bad" | "canceled" | "active"] ["premium" | "enterprise" | "ultimate"].';
} else {
$result = array('error_msg' => 'Missing license in body.');
$code = 401;
}
} else if(preg_match('/checkout.*/', $_GET['path'])) {
$result = array();
$result['zipCode'] = false;
$result['allowRememberMe'] = false;
$result['image'] = 'https://' . $_SERVER['HTTP_HOST'] . '/logo.png';
$result['key'] = null; //Insert here a key to unlock the stripe store (is a string). And buy the subscription...
$result['plans'] = array();
$result['plans']['premium'] = array();
$result['plans']['premium']['amount'] = 42;
$result['plans']['enterprise'] = array();
$result['plans']['enterprise']['amount'] = 42;
$result['plans']['enterprise_plus'] = array();
$result['plans']['enterprise_plus']['amount'] = 42;
} else if(preg_match('/auth\/.*/', $_GET['path'])) {
$result = array('error' => 'Sorry, but SSO is currently not supported.');
$code = 401; //Let Pritunl fail, without 500 codes (it will show 405)
} else if(count($pathParts) > 0 && $pathParts[0] == 'checkout') {
$result = array(
'zipCode' => false,
'allowRememberMe' => false,
'image' => 'https://' . $_SERVER['HTTP_HOST'] . '/logo.png',
'key' => null, // Insert here a key to unlock the stripe store (is a string). And buy the subscription...
'plans' => array(
'premium' => array(
'amount' => $licenseCosts
),
'enterprise' => array(
'amount' => $licenseCosts
),
'enterprise_plus' => array(
'amount' => $licenseCosts
)
)
);
}
}
@@ -128,7 +169,7 @@ echo json_encode($result);
// Should we log any request? Used for the development and debugging of this API
if(false) {
// Log request
file_put_contents('access.log', "\n" . date('r') . ":\t" . json_encode(array('head' => getallheaders(), 'body' => file_get_contents('php://input'), 'get' => $_GET, 'post' => $_POST, 'answer_code' => $code, 'answer' => $result)) . "\n", FILE_APPEND);
file_put_contents('access.log', "\n" . date('r') . ":\n" . json_encode(array('head' => getallheaders(), 'body' => file_get_contents('php://input'), 'get' => $_GET, 'post' => $_POST, 'answer_code' => $code, 'answer' => $result)) . "\n", FILE_APPEND);
// GET operator to clear log file
if(isset($_GET['clear']))

12
www/premium.css Normal file
View File

@@ -0,0 +1,12 @@
/* Fixes for the premium subscription-modal, which seems to be empty / broken in recent versions */
.enterprise-modal .enterprise-info {
display: inherit;
}
.enterprise-modal .modal .enterprise-info .premium-plan {
display: inherit;
}
.enterprise-modal .enterprise-buttons {
display: inherit;
}

View File

@@ -1,48 +0,0 @@
/* Fix the sub info */
.enterprise-modal .enterprise-info {
display: block;
}
.enterprise-modal .enterprise-buttons {
display: block;
}
/* And show the enterprise+ (our ultimate) plan */
.enterprise-modal .modal .enterprise-info .enterprise-plus-plan {
display: block;
}
/* @Servers Fix bad spacing? */
.servers-list .server .server-info .server-output-link-viewer {
display: none;
}
.servers-list .server .server-info .server-graph-viewer {
display: none;
}
/* @Servers Fix double shown IPs */
.enterprise-plus .server-routes-list .route .route-network-no-click {
display: none;
}
/* Fix wrongly shown NAT flag */
.enterprise-plus .server-routes-list .route .route-nat {
display: none;
}
.server-routes-list .route .route-nat {
display: none;
}
.server-routes-list .route .route-nat, .server-routes-list .route .route-nat-netmap, .server-routes-list .route .route-net-gateway, .server-routes-list .route .route-default, .server-routes-list .route .route-virtual-network, .server-routes-list .route .route-network-link, .server-routes-list .route .route-server-link, .server-routes-list .route .route-vpc-id {
display: none;
}
/* Fix selectors */
.selector .selector-inner {
display: none;
}
/* Remove second useless Theme selector in settings */
.enterprise-plus .settings-modal .right .form-group.theme {
display: none;
}
/* Hide advanced settings... So the button is useable again... */
.modal .modal-body .advanced {
display: none;
}
/* Hide admins tab, it will be shown only if the user is a super-user */
.nav .admins {
display: none;
}