diff --git a/backend/internal/stream.js b/backend/internal/stream.js
index f69a5c1..4d49bc3 100644
--- a/backend/internal/stream.js
+++ b/backend/internal/stream.js
@@ -9,7 +9,7 @@ const internalHost = require('./host');
const {castJsonIfNeed} = require('../lib/helpers');
function omissions () {
- return ['is_deleted'];
+ return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
}
const internalStream = {
diff --git a/backend/models/stream.js b/backend/models/stream.js
index 40fd601..dbec2dc 100644
--- a/backend/models/stream.js
+++ b/backend/models/stream.js
@@ -8,6 +8,7 @@ const now = require('./now_helper');
Model.knex(db);
const boolFields = [
+ 'enabled',
'is_deleted',
'tcp_forwarding',
'udp_forwarding',
diff --git a/backend/schema/components/stream-object.json b/backend/schema/components/stream-object.json
index 0ab8f90..848c30e 100644
--- a/backend/schema/components/stream-object.json
+++ b/backend/schema/components/stream-object.json
@@ -19,9 +19,7 @@
"incoming_port": {
"type": "integer",
"minimum": 1,
- "maximum": 65535,
- "if": {"properties": {"tcp_forwarding": {"const": true}}},
- "then": {"not": {"oneOf": [{"const": 80}, {"const": 443}]}}
+ "maximum": 65535
},
"forwarding_host": {
"anyOf": [
@@ -60,6 +58,19 @@
},
"meta": {
"type": "object"
+ },
+ "owner": {
+ "$ref": "./user-object.json"
+ },
+ "certificate": {
+ "oneOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "./certificate-object.json"
+ }
+ ]
}
}
}
diff --git a/docker/docker-compose.ci.yml b/docker/docker-compose.ci.yml
index 022f281..280a054 100644
--- a/docker/docker-compose.ci.yml
+++ b/docker/docker-compose.ci.yml
@@ -22,6 +22,10 @@ services:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
+ expose:
+ - '80-81/tcp'
+ - '443/tcp'
+ - '1500-1503/tcp'
networks:
fulltest:
aliases:
@@ -97,7 +101,7 @@ services:
HTTP_PROXY: 'squid:3128'
HTTPS_PROXY: 'squid:3128'
volumes:
- - 'cypress_logs:/results'
+ - 'cypress_logs:/test/results'
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
command: cypress run --browser chrome --config-file=cypress/config/ci.js
diff --git a/docker/scripts/install-s6 b/docker/scripts/install-s6
index 2922735..5f3b73e 100755
--- a/docker/scripts/install-s6
+++ b/docker/scripts/install-s6
@@ -8,7 +8,7 @@ BLUE='\E[1;34m'
GREEN='\E[1;32m'
RESET='\E[0m'
-S6_OVERLAY_VERSION=3.1.5.0
+S6_OVERLAY_VERSION=3.2.0.2
TARGETPLATFORM=${1:-linux/amd64}
# Determine the correct binary file for the architecture given
diff --git a/test/cypress/Dockerfile b/test/cypress/Dockerfile
index c0bc0ba..9b835fe 100644
--- a/test/cypress/Dockerfile
+++ b/test/cypress/Dockerfile
@@ -1,6 +1,4 @@
-FROM cypress/included:13.9.0
-
-COPY --chown=1000 ./test /test
+FROM cypress/included:14.0.1
# Disable Cypress CLI colors
ENV FORCE_COLOR=0
@@ -12,12 +10,13 @@ RUN wget "https://github.com/testssl/testssl.sh/archive/refs/tags/v3.2rc4.tar.gz
&& mv /tmp/testssl.sh-3.2rc4 /testssl \
&& rm /tmp/testssl.tgz \
&& apt-get update \
- && apt-get install -y bsdmainutils \
+ && apt-get install -y bsdmainutils curl dnsutils \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& wget "https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64" -O /bin/mkcert \
&& chmod +x /bin/mkcert
+COPY --chown=1000 ./test /test
WORKDIR /test
RUN yarn install && yarn cache clean
ENTRYPOINT []
diff --git a/test/cypress/e2e/api/Streams.cy.js b/test/cypress/e2e/api/Streams.cy.js
new file mode 100644
index 0000000..9de4315
--- /dev/null
+++ b/test/cypress/e2e/api/Streams.cy.js
@@ -0,0 +1,213 @@
+///
yay it works
' + }, + }, + }).then((data) => { + cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data); + }); + }); + + // Create a custom cert pair + cy.exec('mkcert -cert-file=/test/cypress/fixtures/website1.pem -key-file=/test/cypress/fixtures/website1.key.pem website1.example.com').then((result) => { + expect(result.code).to.eq(0); + // Install CA + cy.exec('mkcert -install').then((result) => { + expect(result.code).to.eq(0); + }); + }); + + cy.exec('rm -f /test/results/testssl.json'); + }); + + it('Should be able to create TCP Stream', function() { + cy.task('backendApiPost', { + token: token, + path: '/api/nginx/streams', + data: { + incoming_port: 1500, + forwarding_host: '127.0.0.1', + forwarding_port: 80, + certificate_id: 0, + meta: { + dns_provider_credentials: "", + letsencrypt_agree: false, + dns_challenge: true + }, + tcp_forwarding: true, + udp_forwarding: false + } + }).then((data) => { + cy.validateSwaggerSchema('post', 201, '/nginx/streams', data); + expect(data).to.have.property('id'); + expect(data.id).to.be.greaterThan(0); + expect(data).to.have.property('enabled', true); + expect(data).to.have.property('tcp_forwarding', true); + expect(data).to.have.property('udp_forwarding', false); + + cy.exec('curl --noproxy -- http://website1.example.com:1500').then((result) => { + expect(result.code).to.eq(0); + expect(result.stdout).to.contain('yay it works'); + }); + }); + }); + + it('Should be able to create UDP Stream', function() { + cy.task('backendApiPost', { + token: token, + path: '/api/nginx/streams', + data: { + incoming_port: 1501, + forwarding_host: '127.0.0.1', + forwarding_port: 80, + certificate_id: 0, + meta: { + dns_provider_credentials: "", + letsencrypt_agree: false, + dns_challenge: true + }, + tcp_forwarding: false, + udp_forwarding: true + } + }).then((data) => { + cy.validateSwaggerSchema('post', 201, '/nginx/streams', data); + expect(data).to.have.property('id'); + expect(data.id).to.be.greaterThan(0); + expect(data).to.have.property('enabled', true); + expect(data).to.have.property('tcp_forwarding', false); + expect(data).to.have.property('udp_forwarding', true); + }); + }); + + it('Should be able to create TCP/UDP Stream', function() { + cy.task('backendApiPost', { + token: token, + path: '/api/nginx/streams', + data: { + incoming_port: 1502, + forwarding_host: '127.0.0.1', + forwarding_port: 80, + certificate_id: 0, + meta: { + dns_provider_credentials: "", + letsencrypt_agree: false, + dns_challenge: true + }, + tcp_forwarding: true, + udp_forwarding: true + } + }).then((data) => { + cy.validateSwaggerSchema('post', 201, '/nginx/streams', data); + expect(data).to.have.property('id'); + expect(data.id).to.be.greaterThan(0); + expect(data).to.have.property('enabled', true); + expect(data).to.have.property('tcp_forwarding', true); + expect(data).to.have.property('udp_forwarding', true); + + cy.exec('curl --noproxy -- http://website1.example.com:1502').then((result) => { + expect(result.code).to.eq(0); + expect(result.stdout).to.contain('yay it works'); + }); + }); + }); + + it('Should be able to create SSL TCP Stream', function() { + let certID = 0; + + // Create custom cert + cy.task('backendApiPost', { + token: token, + path: '/api/nginx/certificates', + data: { + provider: "other", + nice_name: "Custom Certificate for SSL Stream", + }, + }).then((data) => { + cy.validateSwaggerSchema('post', 201, '/nginx/certificates', data); + expect(data).to.have.property('id'); + certID = data.id; + + // Upload files + cy.task('backendApiPostFiles', { + token: token, + path: `/api/nginx/certificates/${certID}/upload`, + files: { + certificate: 'website1.pem', + certificate_key: 'website1.key.pem', + }, + }).then((data) => { + cy.validateSwaggerSchema('post', 200, '/nginx/certificates/{certID}/upload', data); + expect(data).to.have.property('certificate'); + expect(data).to.have.property('certificate_key'); + + // Create the stream + cy.task('backendApiPost', { + token: token, + path: '/api/nginx/streams', + data: { + incoming_port: 1503, + forwarding_host: '127.0.0.1', + forwarding_port: 80, + certificate_id: certID, + meta: { + dns_provider_credentials: "", + letsencrypt_agree: false, + dns_challenge: true + }, + tcp_forwarding: true, + udp_forwarding: false + } + }).then((data) => { + cy.validateSwaggerSchema('post', 201, '/nginx/streams', data); + expect(data).to.have.property('id'); + expect(data.id).to.be.greaterThan(0); + expect(data).to.have.property("enabled", true); + expect(data).to.have.property('tcp_forwarding', true); + expect(data).to.have.property('udp_forwarding', false); + expect(data).to.have.property('certificate_id', certID); + + // Check the ssl termination + cy.task('log', '[testssl.sh] Running ...'); + cy.exec('/testssl/testssl.sh --quiet --add-ca="$(/bin/mkcert -CAROOT)/rootCA.pem" --jsonfile=/test/results/testssl.json website1.example.com:1503', { + timeout: 120000, // 2 minutes + }).then((result) => { + cy.task('log', '[testssl.sh] ' + result.stdout); + + const allowedSeverities = ["INFO", "OK", "LOW", "MEDIUM"]; + const ignoredIDs = [ + 'cert_chain_of_trust', + 'cert_extlifeSpan', + 'cert_revocation', + 'overall_grade', + ]; + + cy.readFile('/test/results/testssl.json').then((data) => { + // Parse each array item + for (let i = 0; i < data.length; i++) { + const item = data[i]; + if (ignoredIDs.includes(item.id)) { + continue; + } + expect(item.severity).to.be.oneOf(allowedSeverities); + } + }); + }); + }); + }); + }); + }); + +}); diff --git a/test/package.json b/test/package.json index b52ecfb..a3a3cad 100644 --- a/test/package.json +++ b/test/package.json @@ -4,18 +4,18 @@ "description": "", "main": "index.js", "dependencies": { - "@jc21/cypress-swagger-validation": "^0.3.1", - "axios": "^1.7.7", - "cypress": "^13.15.0", - "cypress-multi-reporters": "^1.6.4", + "@jc21/cypress-swagger-validation": "^0.3.2", + "axios": "^1.7.9", + "cypress": "^14.0.1", + "cypress-multi-reporters": "^2.0.5", "cypress-wait-until": "^3.0.2", - "eslint": "^9.12.0", + "eslint": "^9.19.0", "eslint-plugin-align-assignments": "^1.1.2", "eslint-plugin-chai-friendly": "^1.0.1", - "eslint-plugin-cypress": "^3.5.0", + "eslint-plugin-cypress": "^4.1.0", "form-data": "^4.0.1", "lodash": "^4.17.21", - "mocha": "^10.7.3", + "mocha": "^11.1.0", "mocha-junit-reporter": "^2.2.1" }, "scripts": {