├── .gitignore
├── ztncui_code@0.8.6
├── build
│ ├── before-upgrade.sh
│ ├── after-remove.sh
│ ├── .gitignore
│ ├── before-remove.sh
│ ├── ztncui.service
│ ├── before-install.sh
│ ├── binding.gyp.patch
│ ├── after-upgrade.sh
│ ├── after-install.sh
│ ├── build.sh
│ └── openssl.cnf
└── src
│ ├── etc
│ ├── .gitignore
│ ├── tls
│ │ └── .gitignore
│ └── default.passwd
│ ├── public
│ ├── .well-known
│ │ └── acme-challenge
│ │ │ └── .gitignore
│ ├── favicon.ico
│ ├── images
│ │ └── key-logo.svg
│ └── stylesheets
│ │ └── style.css
│ ├── .gitignore
│ ├── views
│ ├── error.pug
│ ├── front_door.pug
│ ├── login_layout.pug
│ ├── controller_layout.pug
│ ├── not_implemented.pug
│ ├── users_layout.pug
│ ├── member_detail.pug
│ ├── index.pug
│ ├── network_layout.pug
│ ├── users.pug
│ ├── network_delete.pug
│ ├── v4AssignMode.pug
│ ├── network_create.pug
│ ├── private.pug
│ ├── user_delete.pug
│ ├── member_delete.pug
│ ├── v6AssignMode.pug
│ ├── networks.pug
│ ├── login.pug
│ ├── dns.pug
│ ├── routes.pug
│ ├── head_layout.pug
│ ├── ipAssignmentPools.pug
│ ├── password.pug
│ ├── ipAssignments.pug
│ ├── network_easy.pug
│ └── network_detail.pug
│ ├── controllers
│ ├── token.js
│ ├── auth.js
│ ├── usersController.js
│ ├── zt.js
│ └── networkController.js
│ ├── package.json
│ ├── routes
│ ├── users.js
│ ├── index.js
│ └── zt_controller.js
│ ├── app.js
│ └── bin
│ └── www
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .PrivateProjects
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/before-upgrade.sh:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/etc/.gitignore:
--------------------------------------------------------------------------------
1 | passwd
2 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/after-remove.sh:
--------------------------------------------------------------------------------
1 | systemctl daemon-reload
2 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/.gitignore:
--------------------------------------------------------------------------------
1 | Release/
2 | Staging/
3 | ztncui
4 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/etc/tls/.gitignore:
--------------------------------------------------------------------------------
1 | fullchain.pem
2 | privkey.pem
3 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/public/.well-known/acme-challenge/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/before-remove.sh:
--------------------------------------------------------------------------------
1 | systemctl stop ztncui
2 | systemctl disable ztncui
3 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | etc/passwd
3 | etc/storage/
4 | node_modules/
5 | *.swp
6 | .env
7 | ztncui
8 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/error.pug:
--------------------------------------------------------------------------------
1 | extends controller_layout
2 |
3 | block content
4 | h1= message
5 | h2= error.status
6 | pre #{error.stack}
7 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TcDhlPro/ZeroTierOne-SelfHostingNetworkControllers-ZtnCui/HEAD/ztncui_code@0.8.6/src/public/favicon.ico
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/etc/default.passwd:
--------------------------------------------------------------------------------
1 | {"admin":{"name":"admin","pass_set":false,"hash":"$argon2i$v=19$m=4096,t=3,p=1$/VYxjWHBzbkuCEO6Hh0AUw$nJaTJtth57vCAyYvg+UbtnscilR0UcE02AfLOhERe3A"}}
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/ztncui.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=ztncui - ZeroTier network controller user interface
3 | Documentation=https://key-networks.com
4 | After=network.target
5 |
6 | [Service]
7 | Type=simple
8 | User=ztncui
9 | WorkingDirectory=/opt/key-networks/ztncui
10 | ExecStart=/opt/key-networks/ztncui/ztncui
11 | Restart=on-failure
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/front_door.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends login_layout
7 |
8 | block login_content
9 | h1!= title
10 |
11 | h2
12 | a(href='https://zerotier.com' target='_blank') ZeroTier
13 | | network controller UI
14 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/before-install.sh:
--------------------------------------------------------------------------------
1 | getent passwd ztncui || useradd --system --home-dir /opt/key-networks/ztncui --shell /bin/false ztncui
2 | if [ $(getent group zerotier-one) ]; then
3 | echo "Adding user ztncui to group zerotier-one..."
4 | usermod -a -G zerotier-one ztncui
5 | chmod g+r /var/lib/zerotier-one/authtoken.secret
6 | else
7 | echo "Could not add user ztncui to group zerotier-one... is zerotier-one installed?"
8 | fi
9 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/binding.gyp.patch:
--------------------------------------------------------------------------------
1 | --- ../src/node_modules/argon2/binding.gyp
2 | +++ ../src/node_modules/argon2/binding.gyp
3 | @@ -47,6 +47,7 @@
4 | ],
5 | "cflags+": ["-Wno-cast-function-type"],
6 | "include_dirs+": ["#{zt_status.address}
20 | h4 ZeroTier version #{zt_status.version}
21 | h4
22 | a(href='/controller/networks') List all networks on this network controller
23 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/network_layout.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends controller_layout
7 |
8 | block content
9 | if error
10 | b #{error}
11 | else
12 | .row
13 | .col-sm-10
14 | block network_title
15 | h2
16 | | Network
17 | a(href='/controller/network/' + network.nwid) #{network.name}
18 | | (#{network.nwid}):
19 | block title
20 | if title
21 | h3= title
22 |
23 | .col-sm-2
24 | h2.right
25 | a.btn.btn-default(href=navigate.whence role='button') Back
26 | block net_content
27 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/users.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends users_layout
7 |
8 | block users_content
9 | table.table.table-responsive.table-striped.table-hover
10 | each user in users
11 | tr
12 | td(width='3%')
13 | a(href='/users/' + user.name + '/delete')
14 | i.glyphicon.glyphicon-trash
15 | td(width='15%')
16 | a(href='/users/' + user.name + '/password') #{user.name}
17 | td(width='82%')
18 | a(href='/users/' + user.name + '/password') set password
19 |
20 | else
21 | .alert.alert-info
22 | strong There are no users on this system
23 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/network_delete.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | if network.deleted
10 | .alert.alert-success
11 | strong #{network.name} (#{network.nwid}) was deleted
12 |
13 | else
14 | .alert.alert-danger
15 | strong Warning! Deleting a network cannot be undone
16 | form(method='POST' action='')
17 | button.btn.btn-danger(type='submit', name='delete') Delete #{network.name} (#{network.nwid})
18 | = ' '
19 | a.btn.btn-default(href='/controller/networks', name='cancel', role='button') Cancel
20 |
21 | if errors
22 | ul
23 | for err in errors
24 | li!= err.msg
25 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/v4AssignMode.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | script.
10 | $(function() {
11 | $('.checkbox').on('click', function() {
12 | $.post('', {'zt': $('#v4AssignModeCheckBox').is(':checked')});
13 | });
14 | });
15 |
16 | form(method='POST' action='')
17 | table.table.table-responsive.table-striped.table-hover
18 | tr
19 | td(width='3%')
20 | input#v4AssignModeCheckBox.checkbox(type='checkbox' checked=(network.v4AssignMode.zt? true : false))
21 | td Auto-assign from IP Assignment Pool
22 |
23 | if errors
24 | ul
25 | for err in errors
26 | li!= err.msg
27 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/after-install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ETC='/opt/key-networks/ztncui/etc'
4 | if [ -f ${ETC}/passwd ]; then
5 | echo "Password file aready exists"
6 | else
7 | echo "Copying default password file..."
8 | cp -pv ${ETC}/default.passwd ${ETC}/passwd
9 | fi
10 | if [ -f /opt/key-networks/ztncui/etc/tls/privkey.pem ] && [ -f /opt/key-networks/ztncui/etc/tls/fullchain.pem ]; then
11 | echo "TLS key and certificate already exist"
12 | else
13 | echo "Generating new TLS key and self-signed certificate..."
14 | openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout /opt/key-networks/ztncui/etc/tls/privkey.pem -out /opt/key-networks/ztncui/etc/tls/fullchain.pem -subj "/C=XX/ST=YY/L=ZZ/O=Security/OU=SelfSigned/CN=example.com"
15 | fi
16 | chown ztncui.ztncui /opt/key-networks/ztncui/etc/tls/*
17 | echo "Enabling and starting ztncui service..."
18 | systemctl enable ztncui
19 | systemctl start ztncui
20 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/network_create.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends controller_layout
7 |
8 | block content
9 | .row
10 | .col-sm-12
11 | h1= title
12 |
13 | if error
14 | b #{error}
15 | else
16 |
17 | form(method='POST' action='')
18 | .form-group.row
19 | .col-sm-2
20 | label(for='name') Network name:
21 | .col-sm-10
22 | input#name.form-control(type='text' name='name' placeholder='Enter new network name' value=(undefined===name ? '' : name.name))
23 |
24 | .form-group.row
25 | .col-sm-12
26 | button.btn.btn-primary(type='submit') Create Network
27 |
28 | if errors
29 | .row
30 | .col-sm-12
31 | ul
32 | for err in errors
33 | li!= err.msg
34 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/private.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | script.
10 | $(function() {
11 | $('.checkbox').on('click', function() {
12 | $.post('', {'private': $('#privateCheckBox').is(':checked')});
13 | });
14 | });
15 |
16 | form(method='POST' action='')
17 | table.table.table-responsive.table-striped.table-hover
18 | tr
19 | td(width='3%')
20 | input#privateCheckBox.checkbox(type='checkbox' checked=(network.private? true : false))
21 | td Enable access control. Warning: if you disable this, you will not be able to de-authorize members of the network. Disable this only if you know what you are doing.
22 |
23 | if errors
24 | ul
25 | for err in errors
26 | li!= err.msg
27 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/user_delete.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends users_layout
7 |
8 | block users_content
9 | if user === null
10 | .alert.alert-warning
11 | strong No such user
12 |
13 | else if self_delete === true
14 | .alert.alert-danger
15 | strong You may not delete yourself
16 |
17 | else if deleted
18 | .alert.alert-success
19 | strong #{user.name} was deleted
20 |
21 | else
22 | .alert.alert-danger
23 | strong Warning! Deleting a user cannot be undone
24 | form(method='POST' action='')
25 | button.btn.btn-danger(type='submit', name='delete' value='delete') Delete #{user.name}
26 | = ' '
27 | a.btn.btn-default(href='/users', name='cancel', role='button') Cancel
28 |
29 | if errors
30 | ul
31 | for err in errors
32 | li!= err.msg
33 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ztncui",
3 | "version": "0.8.6",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www",
7 | "devstart": "nodemon ./bin/www"
8 | },
9 | "dependencies": {
10 | "argon2": "^0.19.3",
11 | "body-parser": "^1.18.3",
12 | "bootstrap": "^3.4.1",
13 | "cookie-parser": "~1.4.3",
14 | "debug": "~3.1.0",
15 | "dotenv": "^4.0.0",
16 | "express": "^4.16.3",
17 | "express-session": "^1.15.6",
18 | "express-validator": "^4.3.0",
19 | "got": "^7.1.0",
20 | "helmet": "^3.23.0",
21 | "ip-address": "^5.8.9",
22 | "jquery": "~3.4.1",
23 | "morgan": "~1.9.1",
24 | "node-persist": "^2.1.0",
25 | "pug": "^3.0.2",
26 | "serve-favicon": "~2.5.0"
27 | },
28 | "devDependencies": {
29 | "nodemon": "^2.0.7"
30 | },
31 | "pkg": {
32 | "assets": [
33 | "views/*",
34 | "public/**/*",
35 | "etc/**/*",
36 | "node_modules/jquery/dist/jquery.min.js"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/member_delete.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | if member.deleted
10 | .alert.alert-success
11 | strong #{member.name} (#{member.id}) was deleted
12 | a.btn.btn-default(href=('/controller/network/' + network.nwid + '#members') name='networks' role='button') Members
13 |
14 | else
15 | .alert.alert-info
16 | strong
17 | ul
18 | li To undo a member deletion, just get the member to join the network again.
19 | li After deleting a member, you may see them appear in the list of members again. This is a ZeroTier issue. Just get the user to leave the network.
20 |
21 | form(method='POST' action='')
22 | button.btn.btn-primary(type='submit', name='delete') Delete #{member.name} (#{member.id})
23 | = ' '
24 | a.btn.btn-default(href='/controller/network/' + network.nwid + '#members',
25 | name='cancel', role='button') Cancel
26 |
27 | if errors
28 | ul
29 | for err in errors
30 | li!= err.msg
31 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/routes/users.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | const express = require('express');
8 | const router = express.Router();
9 | const auth = require('../controllers/auth');
10 | const restrict = auth.restrict;
11 | const usersController = require('../controllers/usersController');
12 |
13 | // GET request for users
14 | router.get('/', restrict, usersController.users_list);
15 |
16 | // GET request for password
17 | router.get('/:name/password', restrict, usersController.password_get);
18 |
19 | // POST request for password
20 | router.post('/:name/password', restrict, usersController.password_post);
21 |
22 | // GET request for user create
23 | router.get('/create', restrict, usersController.user_create_get);
24 |
25 | // POST request for user create
26 | router.post('/create', restrict, usersController.user_create_post);
27 |
28 | // GET request for user delete
29 | router.get('/:name/delete', restrict, usersController.user_delete);
30 |
31 | // POST request for user delete
32 | router.post('/:name/delete', restrict, usersController.user_delete);
33 |
34 | module.exports = router;
35 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/v6AssignMode.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | script.
10 | $(function() {
11 | $('.checkbox').on('click', function() {
12 | $.post('', {'6plane': $('#6planeCheckBox').is(':checked'), 'rfc4193': $('#rfc4193CheckBox').is(':checked'), 'zt': $('#ztCheckBox').is(':checked')});
13 | });
14 | });
15 |
16 | form(method='POST' action='')
17 | table.table.table-responsive.table-striped.table-hover
18 | tr
19 | td(width='3%')
20 | input#6planeCheckBox.checkbox(type='checkbox' checked=(network.v6AssignMode['6plane']? true : false))
21 | td ZT 6plane (/80 routable for each device)
22 | tr
23 | td
24 | input#rfc4193CheckBox.checkbox(type='checkbox' checked=(network.v6AssignMode['rfc4193']? true : false))
25 | td ZT rfc4193 (/128 for each device)
26 | tr
27 | td
28 | input#ztCheckBox.checkbox(type='checkbox' checked=(network.v6AssignMode['zt']? true : false))
29 | td Auto-assign from IP Assignment Pool
30 |
31 | if errors
32 | ul
33 | for err in errors
34 | li!= err.msg
35 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/networks.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends controller_layout
7 |
8 | block content
9 | h1= title
10 |
11 | if error
12 | b #{error}
13 | else
14 | table.table.table-responsive.table-striped.table-hover
15 | tr
16 | th(width='3%')
17 | = ''
18 | th(width='20%')
19 | | Network name
20 | th(width='20%')
21 | | Network ID
22 | th(width='8%')
23 | = ''
24 | th(width='12%')
25 | = ''
26 | th(width='37%')
27 | = ''
28 | each network in networks
29 | - const nwurl = '/controller/network/' + network.nwid;
30 | tr
31 | td
32 | a(href=nwurl + '/delete')
33 | i.glyphicon.glyphicon-trash
34 | td
35 | a(href=nwurl) #{network.name}
36 | td
37 | = network.nwid
38 | td
39 | a(href=nwurl) detail
40 | td
41 | a(href=nwurl + '/easy') easy setup
42 | td
43 | a(href=nwurl + "#members") members
44 |
45 | else
46 | .alert.alert-info
47 | strong There are no networks on this network controller - click "Add network" above to create a new network.
48 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/controllers/auth.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | const argon2 = require('argon2');
8 | const usersController = require('../controllers/usersController');
9 |
10 | const hash_check = async function(user, password) {
11 | let verified = false;
12 | try {
13 | var users = await usersController.get_users();
14 | } catch (err) {
15 | throw err;
16 | }
17 | try {
18 | verified = await argon2.verify(users[user].hash, password);
19 | } catch (err) {
20 | throw err;
21 | }
22 | return verified;
23 | }
24 |
25 | exports.authenticate = async function(name, pass, callback) {
26 | try {
27 | var users = await usersController.get_users();
28 | } catch (err) {
29 | throw err;
30 | }
31 | let user = users[name];
32 | if (!user) return callback(new Error('cannot find user'));
33 | let verified = await hash_check(name, pass);
34 | if (verified) {
35 | return callback(null, user);
36 | } else {
37 | return callback(new Error('invalid password'));
38 | }
39 | }
40 |
41 | exports.restrict = function(req, res, next) {
42 | if (req.session.user) {
43 | next();
44 | } else {
45 | req.session.error = 'Access denied!';
46 | res.redirect('/login?redirect=' + encodeURIComponent(req.originalUrl));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/login.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends login_layout
7 |
8 | block login_content
9 |
10 | if error
11 | b #{error}
12 | else
13 | .row
14 | .col-sm-12
15 | h1= title
16 |
17 | if message
18 | .alert.alert-info
19 | strong= message
20 |
21 | form.form-horizontal(method='POST' action='')
22 | .form-group.row
23 | .col-sm-2
24 | label.control-label(for='username') Username:
25 | .col-sm-10
26 | .input-group
27 | span.input-group-addon
28 | i.glyphicon.glyphicon-user
29 | input#username.form-control(type='text' name='username' placeholder='Enter your username')
30 |
31 | .form-group.row
32 | .col-sm-2
33 | label.control-label(for='password') Password:
34 | .col-sm-10
35 | .input-group
36 | span.input-group-addon
37 | i.glyphicon.glyphicon-lock
38 | input#password.form-control(type='password' name='password' placeholder='Enter your password')
39 |
40 | .form-group.row
41 | .col-sm-2
42 | .col-sm-10
43 | button.btn.btn-primary(type='submit') Login
44 | = ' '
45 | a.btn.btn-default(href='/' name='cancel' role='button') Cancel
46 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/dns.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | - const dns = network.dns || {};
10 | if (!dns.domain && !(dns.servers && dns.servers.length > 0))
11 | .row
12 | .col-sm-12
13 | b No DNS configuration on this network.
14 | else
15 | .row
16 | .col-sm-2
17 | b Domain:
18 | .col-sm-10
19 | p= dns.domain
20 | .row
21 | .col-sm-2
22 | b Servers:
23 | .col-sm-10
24 | .row
25 | each server in dns.servers
26 | .col-sm-12= server
27 |
28 | .row
29 | .col-sm-12
30 | h3 Change DNS configuration:
31 |
32 | form(method='POST' action='')
33 | .form-group.row
34 | .col-sm-12
35 | label(for='domain') Domain:
36 | .col-sm-12
37 | input#domain.form-control(type='text' name='domain' value=dns.domain)
38 |
39 | .form-group.row
40 | .col-sm-12
41 | label(for='servers') Servers:
42 | .col-sm-12
43 | textarea#servers.form-control(type='text' name='servers' placeholder='(one IP address per line)')
44 | = !dns.servers ? '' : dns.servers.join('\n')
45 |
46 | .form-group.row
47 | .col-sm-12
48 | button.btn.btn-primary(type='submit') Submit
49 | = ' '
50 | a.btn.btn-default(href=('/controller/network/' + network.nwid) name='cancel' role='button') Cancel
51 |
52 | if errors
53 | .row
54 | .col-sm-12
55 | ul
56 | for err in errors
57 | li!= err.msg
58 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/routes.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | .row
10 | .col-sm-12
11 | table.table.table-responsive.table-striped.table-hover
12 | tr
13 | th
14 | th Target
15 | th Gateway
16 | each route in network.routes
17 | tr
18 | td(width='3%')
19 | a(href='/controller/network/' + network.nwid + '/routes/' + route.target + '/delete')
20 | i.glyphicon.glyphicon-trash
21 | td= route.target
22 | td= route.via
23 |
24 | .row
25 | .col-sm-12
26 | h3 Add new route:
27 |
28 | form(method='POST' action='/controller/network/' + network.nwid + '/routes')
29 | .form-group.row
30 | .col-sm-12
31 | label(for='target') Target:
32 | .col-sm-12
33 | input#target.form-control(type='text' name='target' placeholder='e.g. 10.11.12.0/24' value=(undefined===route? '' : route.target))
34 |
35 | .form-group.row
36 | .col-sm-12
37 | label(for='via') Gateway:
38 | .col-sm-12
39 | input#via.form-control(type='text' name='via' placeholder='e.g. 172.16.2.1 or leave blank if the target is the ZT network' value=(undefined===route? '' : route.via))
40 |
41 | .form-group.row
42 | .col-sm-12
43 | button.btn.btn-primary(type='submit') Submit
44 | = ' '
45 | a.btn.btn-default(href=('/controller/network/' + network.nwid) name='cancel' role='button') Cancel
46 |
47 | if errors
48 | .row
49 | .col-sm-12
50 | ul
51 | for err in errors
52 | li!= err.msg
53 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/head_layout.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | mixin nav_item(name, displayName, href)
7 | li(class=((navigate && navigate.active === name) ? 'active' : ''))
8 | a(href=href) #{displayName}
9 |
10 | mixin json_value(value)
11 | - if ((!!value ) && (value.constructor == Object || value.constructor == Array))
12 | code(style='white-space: pre;')= JSON.stringify(value, null, 2)
13 | - else
14 | code= value
15 |
16 | doctype html
17 | html(lang='en')
18 | head
19 | title= title
20 | meta(charset='utf-8')
21 | meta(name='viewport', content='width=device-width, initial-scale=1')
22 | link(rel='stylesheet', href='/bscss/bootstrap.min.css')
23 | link(rel='stylesheet', href='/stylesheets/style.css')
24 | script(src='/jqjs/jquery.min.js')
25 | script(src='/bsjs/bootstrap.min.js')
26 | body
27 | nav.navbar.navbar-inverse.navbar-fixed-top
28 | .container-fluid
29 | .navbar-header
30 | button.navbar-toggle(type='button' data-toggle='collapse' data-target='#BarNav')
31 | span.icon-bar
32 | span.icon-bar
33 | span.icon-bar
34 | a.navbar-brand(href='https://key-networks.com' target='_blank')
35 | img(src='/images/key-logo.svg' alt='Key Networks logo' height='25px' width='25px' style='display: inline')
36 | | Key Networks
37 | .collapse.navbar-collapse(id='BarNav')
38 | ul.nav.navbar-nav
39 | block nav_items
40 | ul.nav.navbar-nav.navbar-right
41 | li
42 | block nav_login
43 | a(href='/logout')
44 | span.glyphicon.glyphicon-log-out
45 | | Logout
46 | block body_content
47 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/routes/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | const express = require('express');
8 | const auth = require('../controllers/auth');
9 | const authenticate = auth.authenticate;
10 | const restrict = auth.restrict;
11 | const router = express.Router();
12 |
13 | /** Redirect logged user to controler page */
14 | function guest_only(req, res, next) {
15 | if (req.session.user) {
16 | res.redirect('/controller');
17 | } else {
18 | next();
19 | }
20 | }
21 |
22 | /* GET home page. */
23 | router.get('/', guest_only, function(req, res, next) {
24 | res.render('front_door', {title: 'ztncui'});
25 | });
26 |
27 | router.get('/logout', function(req, res) {
28 | req.session.destroy(function() {
29 | res.redirect('/');
30 | });
31 | });
32 |
33 | router.get('/login', guest_only, function(req, res) {
34 | let message = null;
35 | if (req.session.error) {
36 | if (req.session.error !== 'Access denied!') {
37 | message = req.session.error;
38 | }
39 | } else {
40 | message = req.session.success;
41 | }
42 | res.render('login', { title: 'Login', message: message });
43 | });
44 |
45 | router.post('/login', async function(req, res) {
46 | await authenticate(req.body.username, req.body.password, function(err, user) {
47 | if (user) {
48 | req.session.regenerate(function() {
49 | req.session.user = user;
50 | req.session.success = 'Authenticated as ' + user.name;
51 | if (user.pass_set) {
52 | res.redirect(req.query.redirect || '/controller');
53 | } else {
54 | res.redirect('/users/' + user.name + '/password');
55 | }
56 | });
57 | } else {
58 | req.session.error = 'Authentication failed, please check your username and password.'
59 | res.redirect('/login');
60 | }
61 | });
62 | });
63 | module.exports = router;
64 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/ipAssignmentPools.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | .row
10 | .col-sm-12
11 | table.table.table-responsive.table-striped.table-hover
12 | tr
13 | th
14 | th IP range start
15 | th IP range end
16 | each ipAssignmentPool in network.ipAssignmentPools
17 | tr
18 | td(width='3%')
19 | a(href='/controller/network/' + network.nwid + '/ipAssignmentPools/' + ipAssignmentPool.ipRangeStart + '/' + ipAssignmentPool.ipRangeEnd + '/delete')
20 | i.glyphicon.glyphicon-trash
21 | td= ipAssignmentPool.ipRangeStart
22 | td= ipAssignmentPool.ipRangeEnd
23 |
24 | .row
25 | .col-sm-12
26 | h3 Add new IP Assignment Pool:
27 |
28 | form(method='POST' action='/controller/network/' + network.nwid + '/ipAssignmentPools')
29 | .form-group.row
30 | .col-sm-2
31 | label(for='ipRangeStart') IP range start:
32 | .col-sm-12
33 | input#ipRangeStart.form-control(type='text' name='ipRangeStart' placeholder='IP range start' value=(undefined===ipAssignmentPool? '' : ipAssignmentPool.ipRangeStart))
34 |
35 | .form-group.row
36 | .col-sm-2
37 | label(for='ipRangeEnd') IP range end:
38 | .col-sm-12
39 | input#ipRangeEnd.form-control(type='text' name='ipRangeEnd' placeholder='IP range end' value=(undefined===ipAssignmentPool? '' : ipAssignmentPool.ipRangeEnd))
40 |
41 | .form-group.row
42 | .col-sm-12
43 | button.btn.btn-primary(type='submit') Submit
44 | = ' '
45 | a.btn.btn-default(href='/controller/network/' + network.nwid name='cancel' role='button') Cancel
46 |
47 | if errors
48 | .row
49 | .col-sm-12
50 | ul
51 | for err in errors
52 | li!= err.msg
53 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/password.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends users_layout
7 |
8 | block users_content
9 | if message
10 | .row
11 | .col-sm-12
12 | .alert.alert-info
13 | strong= message
14 |
15 | form.form-horizontal(method='POST' action='')
16 | .form-group.row
17 | .col-sm-2
18 | label(for='username') Username:
19 | .col-sm-10
20 | .input-group
21 | span.input-group-addon
22 | i.glyphicon.glyphicon-user
23 | input#username.form-control(type='text' name='username' placeholder='Enter username' value=user.name readonly=readonly)
24 |
25 | .form-group.row
26 | .col-sm-2
27 | label(for='password1') Enter new password:
28 | .col-sm-10
29 | .input-group
30 | span.input-group-addon
31 | i.glyphicon.glyphicon-lock
32 | input#password1.form-control(type='password' name='password1' placeholder='Enter new password' value=(undefined===user.password1? '' : user.password1))
33 |
34 | .form-group.row
35 | .col-sm-2
36 | label(for='password2') Re-enter password:
37 | .col-sm-10
38 | .input-group
39 | span.input-group-addon
40 | i.glyphicon.glyphicon-lock
41 | input#password2.form-control(type='password' name='password2' placeholder='Re-enter password' value=(undefined===user.password2? '' : user.password2))
42 |
43 | .form-group.row
44 | .col-sm-2
45 | label(for='pass_set') Change password on next login:
46 | .col-sm-10
47 | input#pass_set(type='checkbox' name='pass_set' value='check')
48 |
49 | .form-group.row
50 | .col-sm-2
51 | .col-sm-10
52 | button.btn.btn-primary(type='submit') Set password
53 | = ' '
54 | a.btn.btn-default(href='/users' name='cancel' role='button') Cancel
55 |
56 | if errors
57 | .form-group.row
58 | .col-sm-12
59 | ul
60 | for err in errors
61 | li!= err.msg
62 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/ipAssignments.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | if errors
10 | .row
11 | .col-sm-12
12 | .alert.alert-warning
13 | b Note errors listed below
14 |
15 | form(method='POST' action='')
16 |
17 | .row
18 | .col-sm-6
19 | h4 Member name:
20 | b= member.name
21 | .col-sm-6.right
22 | h4 ZeroTier address:
23 | b= member.id
24 |
25 | .row
26 | .col-sm-12
27 | table.table.table-responsive.table-striped.table-hover
28 | tr
29 | th(width='3%')
30 | th IP address
31 |
32 | each ipAssignment, index in member.ipAssignments
33 | tr
34 | td(width='3%')
35 | a.btn.btn-link(role='button' href='/controller/network/' + network.nwid + '/member/' + member.id + '/ipAssignments/' + index + '/delete')
36 | i.glyphicon.glyphicon-trash
37 | td
38 | each digit in ipAssignment
39 | = digit
40 |
41 | tr
42 | td
43 | button.btn.btn-link(type='submit')
44 | i.glyphicon.glyphicon-plus
45 | td
46 | input#ipAddress.form-control(type='text' name='ipAddress' placeholder='IP address' value=(undefined===ipAssignment? '' : ipAssignment.ipAddress))
47 |
48 | .row
49 | .col-sm-12
50 | a(href='/controller/network/' + network.nwid + '/routes')
51 | h3 Managed routes
52 | table.table.table-responsive.table-striped.table-hover
53 | tr
54 | th
55 | th Target
56 | th Gateway
57 | each route in network.routes
58 | tr
59 | td(width='3%')
60 | a.btn.btn-link(role='button' href='/controller/network/' + network.nwid + '/routes/' + route.target + '/delete')
61 | i.glyphicon.glyphicon-trash
62 | td= route.target
63 | td= route.via
64 |
65 | if errors
66 | .row
67 | .col-sm-12
68 | ul
69 | for err in errors
70 | li!= err.msg
71 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | require('dotenv').config();
8 |
9 | const express = require('express');
10 | const path = require('path');
11 | const favicon = require('serve-favicon');
12 | const logger = require('morgan');
13 | const cookieParser = require('cookie-parser');
14 | const bodyParser = require('body-parser');
15 | const expressValidator = require('express-validator');
16 | const session = require('express-session');
17 | const helmet = require('helmet');
18 |
19 | const index = require('./routes/index');
20 | const users = require('./routes/users');
21 | const zt_controller = require('./routes/zt_controller');
22 |
23 | const app = express();
24 |
25 | const session_secret = Math.random().toString(36).substring(2,12);
26 |
27 | // view engine setup
28 | app.set('views', path.join(__dirname, 'views'));
29 | app.set('view engine', 'pug');
30 |
31 | app.use(helmet());
32 | app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
33 | app.use(logger('dev'));
34 | app.use(bodyParser.json());
35 | app.use(bodyParser.urlencoded({ extended: false }));
36 | app.use(session({
37 | resave: false,
38 | saveUninitialized: false,
39 | secret: session_secret
40 | }));
41 | app.use(expressValidator());
42 | app.use(cookieParser());
43 | app.use(express.static(path.join(__dirname, 'public')));
44 | app.use('/fonts', express.static(path.join(__dirname, 'node_modules/bootstrap/fonts')));
45 | app.use('/bscss', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/css')));
46 | app.use('/jqjs', express.static(path.join(__dirname, 'node_modules/jquery/dist')));
47 | app.use('/bsjs', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/js')));
48 |
49 | app.use('/', index);
50 | app.use('/users', users);
51 | app.use('/controller', zt_controller);
52 |
53 | // catch 404 and forward to error handler
54 | app.use(function(req, res, next) {
55 | var err = req.session.error;
56 | var msg = req.session.success;
57 | delete req.session.error;
58 | delete req.session.success;
59 | res.locals.message = '';
60 | if (err) res.locals.message = '
' + err + '
';
61 | if (msg) res.locals.message = '' + msg + '
';
62 | next();
63 | });
64 |
65 | // error handler
66 | app.use(function(err, req, res, next) {
67 | // set locals, only providing error in development
68 | res.locals.message = err.message;
69 | res.locals.error = req.app.get('env') === 'development' ? err : {};
70 |
71 | // render the error page
72 | res.status(err.status || 500);
73 | res.render('error');
74 | });
75 |
76 | module.exports = app;
77 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/public/images/key-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
67 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | font: 16px "Lucida Grande", Helvetica, Arial, sans-serif;
3 | }
4 |
5 | input[type=checkbox] {
6 | transform: scale(1.5);
7 | }
8 |
9 | input[type=radio] {
10 | transform: scale(1.5);
11 | }
12 |
13 | .table > tbody > tr > td {
14 | vertical-align: middle;
15 | }
16 |
17 | .left {
18 | float: left;
19 | text-align: left;
20 | }
21 |
22 | .right {
23 | float: right;
24 | text-align: right;
25 | }
26 |
27 | .navbar-inverse {
28 | background-color: #315b80;
29 | border-color: #23415c;
30 | }
31 | .navbar-inverse .navbar-brand {
32 | color: #ecf0f1;
33 | }
34 | .navbar-inverse .navbar-brand:hover,
35 | .navbar-inverse .navbar-brand:focus {
36 | color: #ffffff;
37 | }
38 | .navbar-inverse .navbar-text {
39 | color: #ecf0f1;
40 | }
41 | .navbar-inverse .navbar-nav > li > a {
42 | color: #ecf0f1;
43 | }
44 | .navbar-inverse .navbar-nav > li > a:hover,
45 | .navbar-inverse .navbar-nav > li > a:focus {
46 | color: #ffffff;
47 | }
48 | .navbar-inverse .navbar-nav > li > .dropdown-menu {
49 | background-color: #315b80;
50 | }
51 | .navbar-inverse .navbar-nav > li > .dropdown-menu > li > a {
52 | color: #ecf0f1;
53 | }
54 | .navbar-inverse .navbar-nav > li > .dropdown-menu > li > a:hover,
55 | .navbar-inverse .navbar-nav > li > .dropdown-menu > li > a:focus {
56 | color: #ffffff;
57 | background-color: #23415c;
58 | }
59 | .navbar-inverse .navbar-nav > li > .dropdown-menu > li.divider {
60 | background-color: #23415c;
61 | }
62 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
63 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
64 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
65 | color: #ffffff;
66 | background-color: #23415c;
67 | }
68 | .navbar-inverse .navbar-nav > .active > a,
69 | .navbar-inverse .navbar-nav > .active > a:hover,
70 | .navbar-inverse .navbar-nav > .active > a:focus {
71 | color: #ffffff;
72 | background-color: #23415c;
73 | }
74 | .navbar-inverse .navbar-nav > .open > a,
75 | .navbar-inverse .navbar-nav > .open > a:hover,
76 | .navbar-inverse .navbar-nav > .open > a:focus {
77 | color: #ffffff;
78 | background-color: #23415c;
79 | }
80 | .navbar-inverse .navbar-toggle {
81 | border-color: #23415c;
82 | }
83 | .navbar-inverse .navbar-toggle:hover,
84 | .navbar-inverse .navbar-toggle:focus {
85 | background-color: #23415c;
86 | }
87 | .navbar-inverse .navbar-toggle .icon-bar {
88 | background-color: #ecf0f1;
89 | }
90 | .navbar-inverse .navbar-collapse,
91 | .navbar-inverse .navbar-form {
92 | border-color: #ecf0f1;
93 | }
94 | .navbar-inverse .navbar-link {
95 | color: #ecf0f1;
96 | }
97 | .navbar-inverse .navbar-link:hover {
98 | color: #ffffff;
99 | }
100 |
101 | @media (max-width: 767px) {
102 | .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
103 | color: #ecf0f1;
104 | }
105 | .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
106 | .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
107 | color: #ffffff;
108 | }
109 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
110 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
111 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
112 | color: #ffffff;
113 | background-color: #23415c;
114 | }
115 | }
116 |
117 | .navbar {
118 | background-image: none;
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | const app = require('../app');
8 | const debug = require('debug')('ztncui:server');
9 | const http = require('http');
10 | const https = require('https');
11 | const fs = require('fs');
12 |
13 | /**
14 | * Get ports from environment and store in Express.
15 | */
16 |
17 | const http_port = normalizePort(process.env.HTTP_PORT || '3000');
18 | app.set('http_port', http_port);
19 | const https_port = normalizePort(process.env.HTTPS_PORT || null);
20 | app.set('https_port', https_port);
21 |
22 | /**
23 | * Get interface address on which to listen for HTTPS requests from env.
24 | */
25 | const https_host = process.env.HTTPS_HOST || null;
26 | app.set('https_host', https_host);
27 |
28 | /** Create HTTPS server and listen for protocols on interfaces and ports
29 | * according to environment variables as follows:
30 | * Environment variable Protocol Listen On Port
31 | * -------------------- -------- --------- ----
32 | * HTTP localhost 3000
33 | * HTTP_PORT HTTP localhost HTTP_PORT
34 | * HTTP_ALL_INTERFACES HTTP all interfaces HTTP_PORT || 3000
35 | * HTTPS_PORT HTTPS all interfaces HTTPS_PORT
36 | * HTTPS_HOST HTTPS HTTPS_HOST HTTPS_PORT
37 | */
38 |
39 | const http_all_interfaces = process.env.HTTP_ALL_INTERFACES || null;
40 | if (http_all_interfaces) {
41 | console.log('Listening for HTTP requests on port ' + http_port + ' on all interfaces');
42 | app.listen(http_port);
43 | } else {
44 | console.log('Listening for HTTP requests on port ' + http_port + ' on localhost');
45 | app.listen(http_port, 'localhost');
46 | }
47 |
48 | const options = !https_port ? {} : {
49 | cert: fs.readFileSync('etc/tls/fullchain.pem'),
50 | key: fs.readFileSync('etc/tls/privkey.pem')
51 | };
52 |
53 | const server = https.createServer(options, app);
54 |
55 | if (https_port) {
56 | if (https_host) {
57 | console.log('Listening for HTTPS requests on port ' + https_port + ' on address ' + https_host);
58 | } else {
59 | console.log('Listening for HTTPS requests on port ' + https_port + ' on all interfaces');
60 | }
61 | server.listen(https_port, https_host);
62 | }
63 |
64 | server.on('error', onError);
65 | server.on('listening', onListening);
66 |
67 | /**
68 | * Normalize a port into a number, string, or false.
69 | */
70 |
71 | function normalizePort(val) {
72 | const port = parseInt(val, 10);
73 |
74 | if (isNaN(port)) {
75 | // named pipe
76 | return val;
77 | }
78 |
79 | if (port >= 0) {
80 | // port number
81 | return port;
82 | }
83 |
84 | return false;
85 | }
86 |
87 | /**
88 | * Event listener for HTTP/S server "error" event.
89 | */
90 |
91 | function onError(error) {
92 | if (error.syscall !== 'listen') {
93 | throw error;
94 | }
95 |
96 | const bind = typeof http_port === 'string'
97 | ? 'Pipe ' + http_port
98 | : 'Port ' + http_port;
99 |
100 | const sbind = typeof https_port === 'string'
101 | ? 'Pipe ' + https_port
102 | : 'Port ' + https_port;
103 |
104 | // handle specific listen errors with friendly messages
105 | switch (error.code) {
106 | case 'EACCES':
107 | console.error(bind + ' and ' + sbind + ' require elevated privileges');
108 | process.exit(1);
109 | break;
110 | case 'EADDRINUSE':
111 | console.error(bind + ' and/or ' + sbind + ' already in use');
112 | process.exit(1);
113 | break;
114 | default:
115 | throw error;
116 | }
117 | }
118 |
119 | /**
120 | * Event listener for HTTPS server "listening" event.
121 | */
122 |
123 | function onListening() {
124 | const addr = server.address();
125 | const bind = typeof addr === 'string'
126 | ? 'pipe ' + addr
127 | : 'port ' + addr.port;
128 | debug('Listening on ' + bind);
129 | }
130 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | THISDIR=`pwd`
6 | if [ `basename $THISDIR` != 'build' ]; then
7 | echo "Execute `basename $0` from the build directory"
8 | exit 1
9 | fi
10 |
11 | BASE_DIR=`dirname $THISDIR`
12 | SRC_DIR=$BASE_DIR/src
13 | BUILD_DIR=$BASE_DIR/build
14 | PKG_DIR=$BASE_DIR/Release
15 | STAGING_DIR=$BASE_DIR/Staging
16 |
17 | NAME='ztncui'
18 | DESCRIPTION='ZeroTier network controller user interface'
19 | VERSION=`grep version ../src/package.json | cut -f4 -d'"'`
20 | VENDOR='Key Networks'
21 | MAINTAINER='https://key-networks.com/contact'
22 | URL='https://key-networks.com'
23 | LICENSE='GPLv3'
24 |
25 | BINDINGGYP='node_modules/argon2/binding.gyp'
26 |
27 | NODE_VER='v16'
28 |
29 | if [ ! -f /usr/lib/gcc/x86_64-redhat-linux/8/libstdc++.a ]; then
30 | echo "You must install libstdc++-static"
31 | exit 1
32 | fi
33 |
34 | DEPS="rpmbuild rpmsign npm node"
35 |
36 | for DEP in ${DEPS}; do
37 | if ! which ${DEP}; then
38 | echo "Missing dependency ${DEP}"
39 | exit 1
40 | fi
41 | done
42 |
43 | rm -fr $STAGING_DIR && mkdir $STAGING_DIR
44 | rm -fr $PKG_DIR && mkdir $PKG_DIR
45 |
46 | pushd .
47 | cd ../src
48 | pushd .
49 |
50 | NVER=`node --version`
51 | if [[ ${NVER%%.*} != ${NODE_VER} ]]; then
52 | echo "Missing dependency node ${NODE_VER}"
53 | exit 1
54 | fi
55 |
56 | [[ -d ../src/node_modules ]] && rm -fr ../src/node_modules
57 |
58 | npm install
59 |
60 | patch --forward --dry-run --silent $BINDINGGYP $BUILD_DIR/binding.gyp.patch
61 | if [ $? -eq 0 ]; then
62 | echo "Applying patch to $BINDINGGYP..."
63 | patch --forward $BINDINGGYP $BUILD_DIR/binding.gyp.patch
64 | fi
65 | if [ $? -ne 0 ]; then
66 | echo "Failed to patch $BINDINGGYP"
67 | exit 1
68 | fi
69 |
70 | cd node_modules/argon2/
71 | node-gyp rebuild
72 | if [ $? -ne 0 ]; then
73 | echo "Failed to rebuild argon2"
74 | exit 1
75 | fi
76 |
77 | popd
78 | pkg -c ./package.json -t node16-linux-x64 bin/www -o $BUILD_DIR/ztncui
79 |
80 | popd
81 |
82 | install -m 755 -d $STAGING_DIR/opt
83 | install -m 750 -d $STAGING_DIR/opt/key-networks
84 | install -m 750 -d $STAGING_DIR/opt/key-networks/ztncui
85 | install -m 750 -d $STAGING_DIR/opt/key-networks/ztncui/etc
86 | install -m 750 -d $STAGING_DIR/opt/key-networks/ztncui/etc/tls
87 | install -m 750 -d $STAGING_DIR/opt/key-networks/ztncui/node_modules/argon2/build/Release
88 | install -m 755 -d $STAGING_DIR/lib/systemd/system
89 | install -m 600 $SRC_DIR/etc/default.passwd $STAGING_DIR/opt/key-networks/ztncui/etc/default.passwd
90 | install -m 755 $SRC_DIR/node_modules/argon2/build/Release/argon2.node $STAGING_DIR/opt/key-networks/ztncui/node_modules/argon2/build/Release/
91 | install -m 755 $BUILD_DIR/ztncui $STAGING_DIR/opt/key-networks/ztncui/
92 | install -m 644 $BUILD_DIR/ztncui.service $STAGING_DIR/lib/systemd/system
93 |
94 | rm -f $BUILD_DIR/ztncui
95 |
96 | GENERAL_FPM_FLAGS="
97 | --name $NAME
98 | --version $VERSION
99 | --url $URL
100 | --license $LICENSE
101 | --chdir $STAGING_DIR
102 | --package $PKG_DIR
103 | --directories /opt/key-networks
104 | --depends zerotier-one
105 | --depends openssl
106 | --before-install before-install.sh
107 | --after-install after-install.sh
108 | --before-remove before-remove.sh
109 | --after-remove after-remove.sh
110 | --before-upgrade before-upgrade.sh
111 | --after-upgrade after-upgrade.sh
112 | "
113 |
114 | fpm -s dir -t rpm \
115 | $GENERAL_FPM_FLAGS \
116 | --vendor "$VENDOR" \
117 | --maintainer "$MAINTAINER" \
118 | --description "$DESCRIPTION" \
119 | --rpm-user ztncui \
120 | --rpm-group ztncui \
121 | .
122 |
123 | fpm -s dir -t deb \
124 | $GENERAL_FPM_FLAGS \
125 | --vendor "$VENDOR" \
126 | --maintainer "$MAINTAINER" \
127 | --description "$DESCRIPTION" \
128 | --deb-user ztncui \
129 | --deb-group ztncui \
130 | .
131 |
132 | rpm --addsign ../Release/ztncui*rpm
133 | rpm --checksig ../Release/ztncui*rpm
134 |
135 | createrepo $PKG_DIR
136 | gpg -u 'Key Networks' --detach-sign --armor $PKG_DIR/repodata/repomd.xml
137 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/routes/zt_controller.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | const express = require('express');
8 | const auth = require('../controllers/auth');
9 | const restrict = auth.restrict;
10 | const router = express.Router();
11 |
12 | var networkController = require('../controllers/networkController');
13 |
14 | // network routes //
15 |
16 | // GET ZT network controller home page
17 | router.get('/', restrict, networkController.index);
18 |
19 | // Get request for creating a network
20 | router.get('/network/create', restrict, networkController.network_create_get);
21 |
22 | // POST request for creating a network
23 | router.post('/network/create', restrict, networkController.network_create_post);
24 |
25 | // GET request to delete network
26 | router.get('/network/:nwid/delete', restrict, networkController.network_delete_get);
27 |
28 | // POST request to delete network
29 | router.post('/network/:nwid/delete', restrict, networkController.network_delete_post);
30 |
31 | // POST request for Network name
32 | router.post('/network/:nwid/name', restrict, networkController.name);
33 |
34 | // GET request for ipAssignmentPool delete
35 | router.get('/network/:nwid/ipAssignmentPools/:ipRangeStart/:ipRangeEnd/delete', restrict, networkController.ipAssignmentPool_delete);
36 |
37 | // POST request for ipAssignmentPools
38 | router.post('/network/:nwid/ipAssignmentPools', restrict, networkController.ipAssignmentPools);
39 |
40 | // GET request for route delete
41 | router.get('/network/:nwid/routes/:target_ip/:target_prefix/delete', restrict, networkController.route_delete);
42 |
43 | // POST request for routes
44 | router.post('/network/:nwid/routes', restrict, networkController.routes);
45 |
46 | // POST request for dns
47 | router.post('/network/:nwid/dns', restrict, networkController.dns);
48 |
49 | // POST request for private
50 | router.post('/network/:nwid/private', restrict, networkController.private);
51 |
52 | // POST request for v4AssignMode
53 | router.post('/network/:nwid/v4AssignMode', restrict, networkController.v4AssignMode);
54 |
55 | // POST request for v6AssignMode
56 | router.post('/network/:nwid/v6AssignMode', restrict, networkController.v6AssignMode);
57 |
58 | // GET request for member delete
59 | router.get('/network/:nwid/member/:id/delete', restrict, networkController.member_delete);
60 |
61 | // POST request for member delete
62 | router.post('/network/:nwid/member/:id/delete', restrict, networkController.member_delete);
63 |
64 | // GET request for any member object
65 | router.get('/network/:nwid/member/:id/:object', restrict, networkController.member_object);
66 |
67 | // GET request for member detail
68 | router.get('/network/:nwid/member/:id', restrict, networkController.member_detail);
69 |
70 | // GET request for easy network setup
71 | router.get('/network/:nwid/easy', restrict, networkController.easy_get);
72 |
73 | // POST request for easy network setup
74 | router.post('/network/:nwid/easy', restrict, networkController.easy_post);
75 |
76 | // GET request for easy member (de)authorization
77 | router.get('/network/:nwid/members', restrict, networkController.members);
78 |
79 | // POST request for easy member (de)authorization
80 | router.post('/network/:nwid/members', restrict, networkController.members);
81 |
82 | // GET request for member ipAssignment delete
83 | router.get('/network/:nwid/member/:id/ipAssignments/:index/delete', restrict, networkController.delete_ip);
84 |
85 | // POST request for member ipAssignment add
86 | router.post('/network/:nwid/member/:id/ipAssignments', restrict, networkController.assign_ip);
87 |
88 |
89 | // GET request for any network object
90 | router.get('/network/:nwid/:object', restrict, networkController.network_object);
91 |
92 | // GET request for one network
93 | router.get('/network/:nwid', restrict, networkController.network_detail);
94 |
95 | // GET request for list of all networks
96 | router.get('/networks', restrict, networkController.network_list);
97 |
98 | module.exports = router;
99 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/network_easy.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | block net_content
9 | script.
10 | function randomOctet() {
11 | return Math.floor(Math.random() * 255);
12 | }
13 |
14 | function randomIPv4() {
15 | const networkCIDR = document.getElementById('networkCIDR');
16 | const CIDR = '10.' + randomOctet() + '.' + randomOctet() + '.0/24';
17 | networkCIDR.value = CIDR;
18 | CIDRtoPool(CIDR);
19 | }
20 |
21 | function int32toIPv4String(int32) {
22 | let ipv4 = '';
23 | ipv4 = ((int32 & 0xff000000)>>>24).toString();
24 | ipv4 += '.' + ((int32 & 0x00ff0000)>>>16).toString();
25 | ipv4 += '.' + ((int32 & 0x0000ff00)>>>8).toString();
26 | ipv4 += '.' + (int32 & 0x000000ff).toString();
27 | return ipv4;
28 | }
29 |
30 | function CIDRtoPool(CIDR) {
31 | const [start, prefix] = CIDR.split('/');
32 | if (undefined !== start && undefined !== prefix &&
33 | prefix > 0 && prefix < 33 &&
34 | /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(start)
35 | ){
36 | const host32 = ((1 << (32 - parseInt(prefix))) - 1) >>> 0;
37 | const net = start.split('.').map(oct => {return parseInt(oct)});
38 | let net32 = 0 >>> 0;
39 | net32 = (net[0]<<24) + (net[1]<<16) + (net[2]<<8) + (net[3]);
40 | net32 &= ~host32;
41 | bcast32 = net32 + host32;
42 | const networkCIDR = document.getElementById('networkCIDR');
43 | const poolStart = document.getElementById('poolStart');
44 | const poolEnd = document.getElementById('poolEnd');
45 | networkCIDR.value = int32toIPv4String(net32) + '/' + prefix;
46 | poolStart.value = int32toIPv4String(net32 + 1);
47 | poolEnd.value = int32toIPv4String(bcast32 - 1);
48 | } else {
49 | poolStart.value = 'Invalid network CIDR';
50 | poolEnd.value = 'Invalid network CIDR';
51 | }
52 | }
53 |
54 | if message
55 | .alert.alert-info
56 | strong= message
57 |
58 | form(method='POST' action='')
59 | .form-group
60 | button.btn.btn-link.float-right(type='button' data-toggle='collapse' data-target='#help') Help
61 | .collapse(id='help')
62 | p Please note that this utility only supports IPv4 at this stage.
63 | p Use the following button to automatically generate a random network address, otherwise fill in the network address CIDR manually and the IP assignment pool will be automatically calculated for you. You can manually alter these calculated values.
64 |
65 | .form-group
66 | button.btn.btn-primary(id='genIPv4' type='button' onclick='randomIPv4()') Generate network address
67 |
68 | .form-group
69 | label(for='networkCIDR') Network address in CIDR notation
70 | input#networkCIDR.form-control(type='text' name='networkCIDR' onchange='CIDRtoPool(this.value)' placeholder='e.g. 10.11.12.0/24' value=(undefined===network.routes[0]? '' : network.routes[0].target))
71 |
72 | .form-group
73 | label(for='poolStart') Start of IP assignment pool
74 | input#poolStart.form-control(type='text' name='poolStart' placeholder='e.g. 10.11.12.1' value=(undefined===network.ipAssignmentPools[0]? '' : network.ipAssignmentPools[0].ipRangeStart))
75 |
76 | .form-group
77 | label(for='poolEnd') End of IP assignment pool
78 | input#poolEnd.form-control(type='text' name='poolEnd' placeholder='e.g. 10.11.12.254' value=(undefined===network.ipAssignmentPools[0]? '' : network.ipAssignmentPools[0].ipRangeEnd))
79 |
80 | .form-group(style='padding-top: 10px')
81 | button.btn.btn-primary(type='submit') Submit
82 | = ' '
83 | a.btn.btn-default(href=('/controller/network/' + network.nwid) name='cancel' role='button') Cancel
84 |
85 | if errors
86 | ul
87 | for err in errors
88 | li!= err.msg
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### [key-networks/ztncui]汉化及一键安装ZtnCui控制器 - ZeroTier根节点服务端:
2 | ---
3 |
4 | # 适配Debian10.10 / 10.13:
5 | - 全自动安装
6 | - 自动识别主机系统
7 | - 自动识别主机IP,切换对应的下载节点
8 |
9 | # 注意事项及参考:
10 | - 建议用个干干净净的Debian10系统进行安装 ZtnCui控制器 - 根节点服务端
11 | - 测试用的服务器系统为: Debian10.10 64位
12 | - 提供的一键安装脚本只做了适配Debian10系统
13 | - 如果想用Centos, 请自行解决环境搭建相关的问题
14 | - 程序名: [ztncui-0.8.6-1.x86_64.rpm](https://agent-github.08w80.com/https://github.com/TcDhlPro/ZeroTierOne-SelfHostingNetworkControllers-ZtnCui/releases/download/v1.0.0/ztncui-0.8.6-1.x86_64.rpm)
15 | - 证书名: [RPM-KEY-TcDhlProForZtnCui@20230402](https://agent-github.08w80.com/https://github.com/TcDhlPro/ZeroTierOne-SelfHostingNetworkControllers-ZtnCui/releases/download/v1.0.0/RPM-KEY-TcDhlProForZtnCui@20230402)
16 | - 新版本的ZtnCui控制器完全汉化, 会在空闲时间慢慢完成
17 | - 汉化已完成 !
18 |
19 | # 安装步骤
20 | - 进入目录:
21 | ```shell
22 | cd ~
23 | ```
24 | - 下载脚本:
25 | ```shell
26 | wget https://agent-github.08w80.com/https://github.com/TcDhlPro/ZeroTierOne-SelfHostingNetworkControllers-ZtnCui/releases/download/v1.0.0/Auto_Install_ZtncuiForZerotier.tar.gz
27 | ```
28 | - 解包:
29 | ```shell
30 | tar -zxvf Auto_Install_ZtncuiForZerotier.tar.gz
31 | ```
32 | - 给脚本文件夹权限:
33 | ```shell
34 | chmod -R 775 Auto_Install_ZtncuiForZerotier
35 | ```
36 | - 进入目录:
37 | ```shell
38 | cd /root/Auto_Install_ZtncuiForZerotier
39 | ```
40 | - 启动脚本进行自动化安装:
41 | ```shell
42 | ./Auto_Install_ZtncuiForZerotier
43 | ```
44 | - 初次安装时, 脚本一般都会在倒计时后, 主动断开ssh连接, 会有提示, 是为了重新配置环境
45 | - 断开ssh连接后, 重新连接服务器
46 | - 进入目录```cd /root/Auto_Install_ZtncuiForZerotier```
47 | - 运行脚本```./Auto_Install_ZtncuiForZerotier```
48 | - 会有一小段时间配置环境......
49 | - 终端会提示你输入两个自定义端口```[ZtnCui的Https端口] [ZeroTier的TCP/UDP端口]```
50 | - 输入```3000```以外未占用的端口, 因为3000端口是被默认用作ZtnCui-Http
51 | - 要记得在防火墙放开```[ZtnCui的Http端口]```或```[ZtnCui的Https端口]```和```[ZeroTier的TCP/UDP端口]```
52 | - 如果```[ZeroTier的```TCP或UDP```端口]```, 没有在你们服务器上的防火墙中放行, 或没有在安全组中放行, 或者只放行了其中一个, 客户端在替换Planet文件后,大概率是通讯失败的, 常见的情况是:加入自建的网络显示ok, 但服务端那边看不到任何客户端的连接
53 | - 等安装完成, 访问ZtnCui后台地址进行查看
54 |
55 | 在官方仓库 [key-networks/ztncui](https://github.com/key-networks/ztncui.git) 基础上进行汉化并打包成可直接安装的linux包,包含deb和rpm格式(附带了证书RPM-KEY-TcDhlProForZtnCui)的软件包
56 |
57 | ---
58 | ## 修复两项内容
59 | - 编辑日期: 2023年04月02日
60 | - 修复网络界面中, `节点地址`显示多行的Bug
61 | - 修复上一版本在网络界面中, 出现`Cannot read properties of undefined (reading "address")`这个错误
62 | - 修复执行安装程序时, 出现类似`UnicodeEncodeError: 'latin-1' codec can't encode characters in position 8-13: ordinal not in range(256)`的错误
63 |
64 | 
65 | 
66 | 
67 | 
68 | ## 新版ZtnCui(0.8.6)控制器相关截图(汉化完成度98%):
69 | - 编辑日期: 2023年01月10日
70 | - 98%是因为有些没必要汉化
71 |
72 | 
73 | 
74 | 
75 | 
76 | 
77 | 
78 | ## 新版ZtnCui(0.8.6)控制器相关截图(未完全汉化阶段):
79 | - 编辑日期: 2022年
80 |
81 | 
82 | 
83 | 
84 | 
85 |
86 | ---
87 |
88 | ### 客户端替换Planet文件
89 | - 服务器安装完成后会在脚本执行的目录找到planet文件,例如/root下生成的planet文件
90 | - 或者使用```/var/lib/zerotier-one```目录中的planet文件
91 |
92 | ### 重启服务
93 | - linux重启Zerotier: ```service zerotier-one restart```
94 | - win系统重启Zerotier: ```需要在服务中重启ZeroTier One这个服务```
95 |
96 | ### 加入自建的根节点网络
97 | - 客户端执行```zerotier-cli join 网络ID```, 之后就可以在web控制中心找到设备。
98 |
99 | ### 特别注意:安装成功后会变更ZeroTier默认的9993端口为你自己设定的端口
100 | - 初始安装成功后,``` zerotier-cli listpeers```服务端执行命令查看节点列表,如果打印出的节点列表是空的,则安装正确
101 | - 无论是什么样的安装方法,在初始安装完成后打印出来如果有其他节点信息,都是错误的
102 | - 自行部署的Zerotier根服务器,服务端在打印出的节点信息中,应该只显示有效连接的客户端
103 | - 客户端替换planet文件后,在不加入自己根服务器网络的情况下, 打印出的节点列表也是空的
104 | - 加入自己根服务器网络后```zerotier-cli listpeers```客户端执行命令查看节点信息, 如果Planet只有一个,且IP为自己服务器,则安装正确.
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/controllers/usersController.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | const fs = require('fs');
8 | const argon2 = require('argon2');
9 | const util = require('util');
10 |
11 | const passwd_file = 'etc/passwd';
12 | const min_pass_len = 10;
13 |
14 | const readFile = util.promisify(fs.readFile);
15 | const writeFile = util.promisify(fs.writeFile);
16 | const chmod = util.promisify(fs.chmod);
17 |
18 | let _users = null;
19 |
20 | const get_users = async function() {
21 | if (_users) {
22 | return _users;
23 | } else {
24 | try {
25 | _users = JSON.parse(await readFile(passwd_file, 'utf8'));
26 | return _users;
27 | } catch(err) {
28 | throw(err);
29 | }
30 | }
31 | }
32 | exports.get_users = get_users;
33 |
34 | const update_users = async function(users) {
35 | try {
36 | await writeFile(passwd_file, JSON.stringify(users), 'utf8');
37 | await chmod(passwd_file, 0600);
38 |
39 | } catch (err) {
40 | throw err;
41 | }
42 | _users = null;
43 | return await get_users();
44 | }
45 |
46 | exports.users_list = async function(req, res) {
47 | const navigate =
48 | {
49 | active: 'users',
50 | }
51 |
52 | try {
53 | const users = await get_users();
54 | res.render('users', { title: 'Admin users', navigate: navigate, message: 'List of users with admin priviledges', users: users });
55 | } catch (err) {
56 | res.render('users', { title: 'Admin users', navigate: navigate, message: 'Error', users: null, error: 'Error returning list of users: ' + err });
57 | }
58 | }
59 |
60 | exports.password_get = async function(req, res) {
61 | const navigate =
62 | {
63 | active: 'users',
64 | }
65 |
66 | const user =
67 | {
68 | name: req.params.name,
69 | password1: null,
70 | password2: null
71 | };
72 | res.render('password', { title: 'Set password', navigate: navigate, user: user, readonly: true, message: '' });
73 | }
74 |
75 | exports.password_post = async function(req, res) {
76 | const navigate =
77 | {
78 | active: 'users',
79 | }
80 |
81 | req.checkBody('username', 'Username required').notEmpty();
82 | req.sanitize('username').escape();
83 | req.sanitize('username').trim();
84 |
85 | req.checkBody('password1', 'Password required').notEmpty();
86 | req.checkBody('password1', 'Minimum password length is ' + min_pass_len + ' characters').isLength({ min: min_pass_len, max: 160 });
87 |
88 | req.checkBody('password2', 'Please re-enter password').notEmpty();
89 | req.checkBody('password2', 'Minimum password length is ' + min_pass_len + ' characters').isLength({ min: min_pass_len, max: 160 });
90 | req.checkBody('password2', 'Passwords are not the same').equals(req.body.password1);
91 |
92 | const errors = req.validationErrors();
93 |
94 | if (errors) {
95 | const user =
96 | {
97 | name: req.body.username,
98 | password1: req.body.password1,
99 | password2: req.body.password2
100 | };
101 | const message = 'Please check errors below';
102 | res.render('password', { title: 'Set password', navigate: navigate, user: user, readonly: true, message: message, errors: errors });
103 | } else {
104 | let pass_set = true;
105 | if (req.body.pass_set === 'check') pass_set = false;
106 |
107 | const hash = await argon2.hash(req.body.password1);
108 |
109 | const user =
110 | {
111 | name: req.body.username,
112 | pass_set: pass_set,
113 | hash: hash
114 | };
115 |
116 | const passwd_user =
117 | {
118 | [req.body.username]: user
119 | };
120 |
121 | let users = await get_users();
122 | users[req.body.username] = user;
123 |
124 | users = await update_users(users);
125 |
126 | const message = 'Successfully set password for ' + req.body.username;
127 | res.render('password', { title: 'Set password', navigate: navigate, user: user, readonly: true, message: message });
128 | }
129 | }
130 |
131 | exports.user_create_get = async function(req, res) {
132 | const navigate =
133 | {
134 | active: 'create_user',
135 | }
136 |
137 | const user =
138 | {
139 | name: null,
140 | password1: null,
141 | password2: null
142 | };
143 |
144 | res.render('password', { title: 'Create new admin user', navigate: navigate, user: user, readonly: false});
145 | }
146 |
147 | exports.user_create_post = async function(req, res) {
148 | const navigate =
149 | {
150 | active: 'create_user',
151 | }
152 |
153 | res.redirect(307, '/users/' + req.body.username + '/password');
154 | }
155 |
156 | exports.user_delete = async function(req, res) {
157 | const navigate =
158 | {
159 | active: 'users',
160 | }
161 |
162 | try {
163 | var users = await get_users();
164 | } catch (err) {
165 | throw err;
166 | }
167 |
168 | const user = users[req.params.name];
169 |
170 | if (user && (req.session.user.name === user.name)) {
171 | res.render('user_delete', { title: 'Delete user', navigate: navigate, user: user, self_delete: true });
172 | }
173 |
174 | if (req.body.delete === 'delete') {
175 | if (user) {
176 | const deleted_user = { name: user.name };
177 | delete users[user.name];
178 | users = await update_users(users);
179 | res.render('user_delete', { title: 'Deleted user', navigate: navigate, user: deleted_user, deleted: true });
180 | } else {
181 | res.render('user_delete', { title: 'Delete user', navigate: navigate, user: null });
182 | }
183 | } else {
184 | if (user) {
185 | res.render('user_delete', { title: 'Delete user', navigate: navigate, user: user });
186 | } else {
187 | res.render('user_delete', { title: 'Delete user', navigate: navigate, user: null });
188 | }
189 | }
190 | }
191 |
192 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/views/network_detail.pug:
--------------------------------------------------------------------------------
1 | //-
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 |
6 | extends network_layout
7 |
8 | //- Don't display that title
9 | block title
10 |
11 | //- Replace the network title with the editable one
12 | block network_title
13 | h2
14 | | Network
15 | a#change-name(href='#')
16 | span#name= network.name
17 | i.glyphicon.glyphicon-pencil(style='font-size: 20px;')
18 | input#name-input.form-control(type='text' style='width: 200px; display: none;')
19 | | (#{network.nwid}):
20 | script.
21 | $(function() {
22 | var nwurl = '/controller/network/#{network.nwid}';
23 | var name = !{JSON.stringify(network.name)};
24 |
25 | function toggleNameEditor(show) {
26 | $('#change-name').css('display', !show ? '' : 'none');
27 | $('#name-input').css('display', show ? 'inline-block' : 'none');
28 | }
29 |
30 | function submit() {
31 | var newName = $('#name-input').val();
32 | if (newName != name) {
33 | name = newName;
34 | $.post(nwurl + '/name', {'name': name})
35 | .done(function () {
36 | $('#name').text(newName);
37 | });
38 | }
39 | toggleNameEditor(false);
40 | }
41 |
42 | $('#change-name').on('click', function() {
43 | toggleNameEditor(true);
44 | $('#name-input').val(name);
45 | $('#name-input').focus();
46 | });
47 | $('#name-input').on('focusout', submit);
48 | $('#name-input').keypress(function (e) {
49 | if (e.which == 13) submit();
50 | });
51 | });
52 |
53 | block net_content
54 | - const nwurl = '/controller/network/' + network.nwid;
55 |
56 | a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/private') role='button')
57 | = network.private ? "Private" : "Public"
58 | a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/easy') role='button') Easy setup
59 | a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/routes') role='button') Routes
60 | a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/ipAssignmentPools') role='button') Assignment Pools
61 | a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/v4AssignMode') role='button') IPv4 Assign Mode
62 | a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/v6AssignMode') role='button') IPv6 Assign Mode
63 | a.btn.btn-primary(style="margin: 5px" href=(nwurl + '/dns') role='button') DNS
64 |
65 | if (members !== undefined)
66 | script.
67 | $(function() {
68 | const url = "#{nwurl}/members";
69 | $('.authCheck').on('click', function() {
70 | $.post(url, {'id': this.value, 'auth': this.checked});
71 | });
72 | $('.bridgeCheck').on('click', function() {
73 | $.post(url, {'id': this.value, 'activeBridge': this.checked});
74 | });
75 | $('.text').on('change', function() {
76 | $.post(url, {'id': this.name, 'name': this.value});
77 | });
78 | });
79 | h3#members Members (#{members.length})
80 | form(method='POST' action='')
81 | table.table.table-responsive.table-striped.table-hover
82 | tr
83 | td(width='3%')
84 | = ''
85 | td(width='20%')
86 | | Member name
87 | td(width='10%')
88 | | Member ID
89 | td(width='10%')
90 | | Authorized
91 | td(width='10%')
92 | | Active bridge
93 | td(width='17%')
94 | | IP assignment
95 | td(width='17%')
96 | | Peer status
97 | td(width='13%')
98 | | Peer address / latency
99 | each member in members
100 | - const peer = member.peer;
101 | tr
102 | - const url = nwurl + '/member/' + member.id
103 | td
104 | a(href=url + '/delete')
105 | i.glyphicon.glyphicon-trash
106 | td
107 | input.form-control.text(type='text' name=member.id value=member.name)
108 | td
109 | a(href=url) #{member.id}
110 | td
111 | input.authCheck(type='checkbox' value=member.id checked=(member.authorized? true : false))
112 | td
113 | input.bridgeCheck(type='checkbox' value=member.id checked=(member.activeBridge? true : false))
114 | td
115 | each ipAssignment in member.ipAssignments
116 | a(href=nwurl + '/member/' + member.id + '/ipAssignments')
117 | each digit in ipAssignment
118 | = digit
119 | = ' '
120 | else
121 | a(href=nwurl + '/member/' + member.id + '/ipAssignments')
122 | | IP assignment
123 | td
124 | if (peer && peer.latency != -1 && peer.versionMajor != -1)
125 | if (peer.latency != -1)
126 | span(style='color: green;')
127 | | ONLINE (v#{peer.version})
128 | else
129 | span(style='color: orange;')
130 | | RELAY (v#{peer.version})
131 | else if (member.id == zt_address)
132 | span(style='color: green;') CONTROLLER
133 | else
134 | span(style='color: red;') OFFLINE
135 | td
136 | if (peer)
137 | each path in peer.paths
138 | - const [ip, port] = path.address.split('/');
139 | = ip
140 | span(style='color: gray;') /#{port}
141 | = ' '
142 | if (peer.latency != -1)
143 | br
144 | | (#{peer.latency} ms)
145 | else
146 | .alert.alert-info
147 | strong There are no members on this network - invite users to join #{network.nwid}
148 |
149 | a.btn.btn-default(href='' name='refresh' role='button') Refresh
150 |
151 | h3#detail Detail for network
152 | each value, key in network
153 | .row(style='margin: 5px 0;')
154 | .col-sm-2
155 | a(href= network.nwid + '/' + key) #{key}:
156 | .col-sm-10
157 | +json_value(value)
158 |
159 | a.btn.btn-default(href='/controller/networks' name='networks' role='button' style='margin-top: 10px;') Networks
160 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/controllers/zt.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | const got = require('got');
8 | const ipaddr = require('ip-address');
9 | const token = require('./token');
10 |
11 | const ZT_ADDR = process.env.ZT_ADDR || 'localhost:9993';
12 |
13 | const init_options = async function() {
14 | let tok = null;
15 |
16 | try {
17 | tok = await token.get();
18 | } catch (err) {
19 | throw(err);
20 | }
21 |
22 | options = {
23 | json: true,
24 | headers: {
25 | 'X-ZT1-Auth': tok
26 | }
27 | }
28 |
29 | return options;
30 | }
31 |
32 | const get_zt_status = async function() {
33 | const options = await init_options();
34 |
35 | try {
36 | const response = await got(ZT_ADDR + '/status', options);
37 | return response.body;
38 | } catch(err) {
39 | throw(err);
40 | }
41 | }
42 | exports.get_zt_status = get_zt_status;
43 |
44 | const get_zt_address = async function() {
45 | return (await get_zt_status()).address;
46 | }
47 | exports.get_zt_address = get_zt_address;
48 |
49 | exports.network_list = async function() {
50 | const options = await init_options();
51 |
52 | let network = {};
53 | let networks = [];
54 | let nwids = [];
55 |
56 | try {
57 | const response = await got(ZT_ADDR + '/controller/network', options);
58 | nwids = response.body;
59 | } catch(err) {
60 | throw(err);
61 | }
62 |
63 | for (let nwid of nwids) {
64 | try {
65 | const response = await got(ZT_ADDR + '/controller/network/'
66 | + nwid, options);
67 | network = (({name, nwid}) => ({name, nwid}))(response.body);
68 | networks.push(network);
69 | } catch(err) {
70 | throw(err);
71 | }
72 | }
73 | return networks;
74 | }
75 |
76 | const network_detail = async function(nwid) {
77 | const options = await init_options();
78 |
79 | try {
80 | const response = await got(ZT_ADDR + '/controller/network/'
81 | + nwid, options);
82 | return response.body;
83 | } catch(err) {
84 | throw(err);
85 | }
86 | }
87 | exports.network_detail = network_detail;
88 |
89 | exports.network_create = async function(name) {
90 | const options = await init_options();
91 | options.method = 'POST';
92 | options.body = name;
93 |
94 | const zt_address = await get_zt_address();
95 |
96 | try {
97 | const response = await got(ZT_ADDR + '/controller/network/'
98 | + zt_address + '______', options);
99 | return response.body;
100 | } catch(err) {
101 | throw(err);
102 | }
103 | }
104 |
105 | exports.network_delete = async function(nwid) {
106 | const options = await init_options();
107 | options.method = 'DELETE';
108 |
109 | try {
110 | const response = await got(ZT_ADDR + '/controller/network/'
111 | + nwid, options);
112 | response.body.deleted = true;
113 | return response.body;
114 | } catch(err) {
115 | throw(err);
116 | }
117 | }
118 |
119 | exports.ipAssignmentPools = async function(nwid, ipAssignmentPool, action) {
120 | const options = await init_options();
121 | options.method = 'POST';
122 |
123 | const network = await network_detail(nwid);
124 | let ipAssignmentPools = network.ipAssignmentPools;
125 |
126 | if (action === 'add') {
127 | ipAssignmentPools.push(ipAssignmentPool);
128 | } else if (action === 'delete') {
129 | const pool = ipAssignmentPools.find(pool =>
130 | pool.ipRangeStart === ipAssignmentPool.ipRangeStart &&
131 | pool.ipRangeEnd === ipAssignmentPool.ipRangeEnd);
132 | ipAssignmentPools = ipAssignmentPools.filter(p => p != pool);
133 | }
134 |
135 | options.body = { ipAssignmentPools: ipAssignmentPools };
136 |
137 |
138 | try {
139 | const response = await got(ZT_ADDR + '/controller/network/'
140 | + nwid, options);
141 | return response.body;
142 | } catch(err) {
143 | throw(err);
144 | }
145 | }
146 |
147 | exports.ipAssignmentDelete = async function(nwid, id, ipAssignmentIndex) {
148 | const options = await init_options();
149 | options.method = 'POST';
150 |
151 | try {
152 | const member = await member_detail(nwid, id);
153 | const ipAssignments = member.ipAssignments;
154 | ipAssignments.splice(ipAssignmentIndex, 1);
155 | options.body = { ipAssignments: ipAssignments };
156 | const response = await got(ZT_ADDR + '/controller/network/'
157 | + nwid + '/member/' + id, options);
158 | response.body.deleted = true;
159 | return response.body;
160 | } catch(err) {
161 | throw(err);
162 | }
163 | }
164 |
165 | exports.ipAssignmentAdd = async function(nwid, id, ipAssignment) {
166 | const options = await init_options();
167 | options.method = 'POST';
168 |
169 | try {
170 | const member = await member_detail(nwid, id);
171 | const ipAssignments = member.ipAssignments;
172 | ipAssignments.push(ipAssignment.ipAddress);
173 | options.body = { ipAssignments: ipAssignments };
174 | const response = await got(ZT_ADDR + '/controller/network/'
175 | + nwid + '/member/' + id, options);
176 | response.body.added = true;
177 | return response.body;
178 | } catch(err) {
179 | throw(err);
180 | }
181 | }
182 |
183 | exports.routes = async function(nwid, route, action) {
184 | const options = await init_options();
185 | options.method = 'POST';
186 |
187 | const network = await network_detail(nwid);
188 | let routes = network.routes;
189 | route.target = canonicalTarget(route.target);
190 |
191 | const route_to_del = routes.find(rt => canonicalTarget(rt.target) === route.target);
192 |
193 | if (!route_to_del) {
194 | if (action === 'add') {
195 | routes.push(route);
196 | } else if (action === 'delete') {
197 | throw new Error('Cannot delete non-existent route target');
198 | }
199 | } else {
200 | if (action === 'add') {
201 | throw new Error('Route target is not unique');
202 | } else if (action === 'delete') {
203 | routes = routes.filter(rt => rt != route_to_del);
204 | }
205 | }
206 |
207 | options.body = { routes: routes };
208 |
209 |
210 | try {
211 | const response = await got(ZT_ADDR + '/controller/network/'
212 | + nwid, options);
213 | return response.body;
214 | } catch(err) {
215 | throw(err);
216 | }
217 | }
218 |
219 | function canonicalTarget(target) {
220 | const target6 = new ipaddr.Address6(target);
221 | if (target6.isValid()) {
222 | const parts = target.split('/');
223 | return target6.canonicalForm() + '/' + parts[1];
224 | }
225 | return target;
226 | }
227 |
228 | exports.network_object = async function(nwid, object) {
229 | const options = await init_options();
230 | options.method = 'POST';
231 | options.body = object;
232 |
233 | try {
234 | const response = await got(ZT_ADDR + '/controller/network/'
235 | + nwid, options);
236 | return response.body;
237 | } catch(err) {
238 | throw(err);
239 | }
240 | }
241 |
242 | exports.members = async function(nwid) {
243 | const options = await init_options();
244 |
245 | try {
246 | const response = await got(ZT_ADDR + '/controller/network/'
247 | + nwid + '/member', options);
248 | return response.body;
249 | } catch(err) {
250 | throw(err);
251 | }
252 | }
253 |
254 | const member_detail = async function(nwid, id) {
255 | const options = await init_options();
256 |
257 | try {
258 | const response = await got(ZT_ADDR + '/controller/network/'
259 | + nwid + '/member/' + id, options);
260 | return response.body;
261 | } catch(err) {
262 | throw(err);
263 | }
264 | }
265 | exports.member_detail = member_detail;
266 |
267 | exports.member_object = async function(nwid, id, object) {
268 | const options = await init_options();
269 | options.method = 'POST';
270 | options.body = object;
271 |
272 | try {
273 | const response = await got(ZT_ADDR + '/controller/network/'
274 | + nwid + '/member/' + id, options);
275 | return response.body;
276 | } catch(err) {
277 | throw(err);
278 | }
279 | }
280 |
281 | exports.member_delete = async function(nwid, id) {
282 | const options = await init_options();
283 | options.method = 'DELETE';
284 |
285 | try {
286 | const response = await got(ZT_ADDR + '/controller/network/'
287 | + nwid + '/member/' + id, options);
288 | response.body.deleted = true;
289 | return response.body;
290 | } catch(err) {
291 | throw(err);
292 | }
293 | }
294 |
295 | exports.network_easy_setup = async function(nwid,
296 | routes,
297 | ipAssignmentPools,
298 | v4AssignMode) {
299 | const options = await init_options();
300 | options.method = 'POST';
301 | options.body =
302 | {
303 | ipAssignmentPools: ipAssignmentPools,
304 | routes: routes,
305 | v4AssignMode: v4AssignMode
306 | };
307 |
308 | try {
309 | const response = await got(ZT_ADDR + '/controller/network/'
310 | + nwid, options);
311 | return response.body;
312 | } catch(err) {
313 | throw(err);
314 | }
315 | }
316 |
317 | exports.peers = async function() {
318 | const options = await init_options();
319 | const response = await got(ZT_ADDR + '/peer', options);
320 | return response.body;
321 | }
322 |
323 | exports.peer = async function(id) {
324 | const options = await init_options();
325 | try {
326 | const response = await got(ZT_ADDR + '/peer/' + id, options);
327 | return response.body;
328 | } catch (error) {
329 | if (error instanceof got.HTTPError && error.statusCode == 404) {
330 | return null;
331 | }
332 | throw error;
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/build/openssl.cnf:
--------------------------------------------------------------------------------
1 | #
2 | # OpenSSL example configuration file.
3 | # This is mostly being used for generation of certificate requests.
4 | #
5 |
6 | # This definition stops the following lines choking if HOME isn't
7 | # defined.
8 | HOME = .
9 | RANDFILE = $ENV::HOME/.rnd
10 |
11 | # Extra OBJECT IDENTIFIER info:
12 | #oid_file = $ENV::HOME/.oid
13 | oid_section = new_oids
14 |
15 | # To use this configuration file with the "-extfile" option of the
16 | # "openssl x509" utility, name here the section containing the
17 | # X.509v3 extensions to use:
18 | # extensions =
19 | # (Alternatively, use a configuration file that has only
20 | # X.509v3 extensions in its main [= default] section.)
21 |
22 | [ new_oids ]
23 |
24 | # We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
25 | # Add a simple OID like this:
26 | # testoid1=1.2.3.4
27 | # Or use config file substitution like this:
28 | # testoid2=${testoid1}.5.6
29 |
30 | # Policies used by the TSA examples.
31 | tsa_policy1 = 1.2.3.4.1
32 | tsa_policy2 = 1.2.3.4.5.6
33 | tsa_policy3 = 1.2.3.4.5.7
34 |
35 | ####################################################################
36 | [ ca ]
37 | default_ca = CA_default # The default ca section
38 |
39 | ####################################################################
40 | [ CA_default ]
41 |
42 | dir = /etc/pki/CA # Where everything is kept
43 | certs = $dir/certs # Where the issued certs are kept
44 | crl_dir = $dir/crl # Where the issued crl are kept
45 | database = $dir/index.txt # database index file.
46 | #unique_subject = no # Set to 'no' to allow creation of
47 | # several certs with same subject.
48 | new_certs_dir = $dir/newcerts # default place for new certs.
49 |
50 | certificate = $dir/cacert.pem # The CA certificate
51 | serial = $dir/serial # The current serial number
52 | crlnumber = $dir/crlnumber # the current crl number
53 | # must be commented out to leave a V1 CRL
54 | crl = $dir/crl.pem # The current CRL
55 | private_key = $dir/private/cakey.pem# The private key
56 | RANDFILE = $dir/private/.rand # private random number file
57 |
58 | x509_extensions = usr_cert # The extensions to add to the cert
59 |
60 | # Comment out the following two lines for the "traditional"
61 | # (and highly broken) format.
62 | name_opt = ca_default # Subject Name options
63 | cert_opt = ca_default # Certificate field options
64 |
65 | # Extension copying option: use with caution.
66 | # copy_extensions = copy
67 |
68 | # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
69 | # so this is commented out by default to leave a V1 CRL.
70 | # crlnumber must also be commented out to leave a V1 CRL.
71 | # crl_extensions = crl_ext
72 |
73 | default_days = 365 # how long to certify for
74 | default_crl_days= 30 # how long before next CRL
75 | default_md = sha256 # use SHA-256 by default
76 | preserve = no # keep passed DN ordering
77 |
78 | # A few difference way of specifying how similar the request should look
79 | # For type CA, the listed attributes must be the same, and the optional
80 | # and supplied fields are just that :-)
81 | policy = policy_match
82 |
83 | # For the CA policy
84 | [ policy_match ]
85 | countryName = match
86 | stateOrProvinceName = match
87 | organizationName = match
88 | organizationalUnitName = optional
89 | commonName = supplied
90 | emailAddress = optional
91 |
92 | # For the 'anything' policy
93 | # At this point in time, you must list all acceptable 'object'
94 | # types.
95 | [ policy_anything ]
96 | countryName = optional
97 | stateOrProvinceName = optional
98 | localityName = optional
99 | organizationName = optional
100 | organizationalUnitName = optional
101 | commonName = supplied
102 | emailAddress = optional
103 |
104 | ####################################################################
105 | [ req ]
106 | prompt = no
107 | default_bits = 2048
108 | default_md = sha256
109 | default_keyfile = privkey.pem
110 | distinguished_name = req_distinguished_name
111 | attributes = req_attributes
112 | x509_extensions = v3_ca # The extensions to add to the self signed cert
113 |
114 | # Passwords for private keys if not present they will be prompted for
115 | # input_password = secret
116 | # output_password = secret
117 |
118 | # This sets a mask for permitted string types. There are several options.
119 | # default: PrintableString, T61String, BMPString.
120 | # pkix : PrintableString, BMPString (PKIX recommendation before 2004)
121 | # utf8only: only UTF8Strings (PKIX recommendation after 2004).
122 | # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
123 | # MASK:XXXX a literal mask value.
124 | # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
125 | string_mask = utf8only
126 |
127 | # req_extensions = v3_req # The extensions to add to a certificate request
128 |
129 | [ req_distinguished_name ]
130 | countryName = AU
131 |
132 | stateOrProvinceName = Western Australia
133 | #stateOrProvinceName_default = Default Province
134 |
135 | localityName = Perth
136 |
137 | 0.organizationName = Key Networks (https://key-networks.com)
138 |
139 | # we can do this but it is not needed normally :-)
140 | #1.organizationName = Second Organization Name (eg, company)
141 | #1.organizationName_default = World Wide Web Pty Ltd
142 |
143 | organizationalUnitName = Engineering
144 | #organizationalUnitName_default =
145 |
146 | commonName = localhost
147 |
148 | emailAddress = noreply@key-networks.com
149 |
150 | # SET-ex3 = SET extension number 3
151 |
152 | [ req_attributes ]
153 | challengePassword = A challenge password
154 | challengePassword_min = 4
155 | challengePassword_max = 20
156 |
157 | unstructuredName = An optional company name
158 |
159 | [ usr_cert ]
160 |
161 | # These extensions are added when 'ca' signs a request.
162 |
163 | # This goes against PKIX guidelines but some CAs do it and some software
164 | # requires this to avoid interpreting an end user certificate as a CA.
165 |
166 | basicConstraints=CA:FALSE
167 |
168 | # Here are some examples of the usage of nsCertType. If it is omitted
169 | # the certificate can be used for anything *except* object signing.
170 |
171 | # This is OK for an SSL server.
172 | # nsCertType = server
173 |
174 | # For an object signing certificate this would be used.
175 | # nsCertType = objsign
176 |
177 | # For normal client use this is typical
178 | # nsCertType = client, email
179 |
180 | # and for everything including object signing:
181 | # nsCertType = client, email, objsign
182 |
183 | # This is typical in keyUsage for a client certificate.
184 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment
185 |
186 | # This will be displayed in Netscape's comment listbox.
187 | nsComment = "OpenSSL Generated Certificate"
188 |
189 | # PKIX recommendations harmless if included in all certificates.
190 | subjectKeyIdentifier=hash
191 | authorityKeyIdentifier=keyid,issuer
192 |
193 | # This stuff is for subjectAltName and issuerAltname.
194 | # Import the email address.
195 | # subjectAltName=email:copy
196 | # An alternative to produce certificates that aren't
197 | # deprecated according to PKIX.
198 | # subjectAltName=email:move
199 |
200 | # Copy subject details
201 | # issuerAltName=issuer:copy
202 |
203 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
204 | #nsBaseUrl
205 | #nsRevocationUrl
206 | #nsRenewalUrl
207 | #nsCaPolicyUrl
208 | #nsSslServerName
209 |
210 | # This is required for TSA certificates.
211 | # extendedKeyUsage = critical,timeStamping
212 |
213 | [ v3_req ]
214 |
215 | # Extensions to add to a certificate request
216 |
217 | basicConstraints = CA:FALSE
218 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment
219 |
220 | [ v3_ca ]
221 |
222 |
223 | # Extensions for a typical CA
224 |
225 |
226 | # PKIX recommendation.
227 |
228 | subjectKeyIdentifier=hash
229 |
230 | authorityKeyIdentifier=keyid:always,issuer
231 |
232 | basicConstraints = critical,CA:true
233 |
234 | # Key usage: this is typical for a CA certificate. However since it will
235 | # prevent it being used as an test self-signed certificate it is best
236 | # left out by default.
237 | # keyUsage = cRLSign, keyCertSign
238 |
239 | # Some might want this also
240 | # nsCertType = sslCA, emailCA
241 |
242 | # Include email address in subject alt name: another PKIX recommendation
243 | # subjectAltName=email:copy
244 | # Copy issuer details
245 | # issuerAltName=issuer:copy
246 |
247 | # DER hex encoding of an extension: beware experts only!
248 | # obj=DER:02:03
249 | # Where 'obj' is a standard or added object
250 | # You can even override a supported extension:
251 | # basicConstraints= critical, DER:30:03:01:01:FF
252 |
253 | [ crl_ext ]
254 |
255 | # CRL extensions.
256 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
257 |
258 | # issuerAltName=issuer:copy
259 | authorityKeyIdentifier=keyid:always
260 |
261 | [ proxy_cert_ext ]
262 | # These extensions should be added when creating a proxy certificate
263 |
264 | # This goes against PKIX guidelines but some CAs do it and some software
265 | # requires this to avoid interpreting an end user certificate as a CA.
266 |
267 | basicConstraints=CA:FALSE
268 |
269 | # Here are some examples of the usage of nsCertType. If it is omitted
270 | # the certificate can be used for anything *except* object signing.
271 |
272 | # This is OK for an SSL server.
273 | # nsCertType = server
274 |
275 | # For an object signing certificate this would be used.
276 | # nsCertType = objsign
277 |
278 | # For normal client use this is typical
279 | # nsCertType = client, email
280 |
281 | # and for everything including object signing:
282 | # nsCertType = client, email, objsign
283 |
284 | # This is typical in keyUsage for a client certificate.
285 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment
286 |
287 | # This will be displayed in Netscape's comment listbox.
288 | nsComment = "OpenSSL Generated Certificate"
289 |
290 | # PKIX recommendations harmless if included in all certificates.
291 | subjectKeyIdentifier=hash
292 | authorityKeyIdentifier=keyid,issuer
293 |
294 | # This stuff is for subjectAltName and issuerAltname.
295 | # Import the email address.
296 | # subjectAltName=email:copy
297 | # An alternative to produce certificates that aren't
298 | # deprecated according to PKIX.
299 | # subjectAltName=email:move
300 |
301 | # Copy subject details
302 | # issuerAltName=issuer:copy
303 |
304 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
305 | #nsBaseUrl
306 | #nsRevocationUrl
307 | #nsRenewalUrl
308 | #nsCaPolicyUrl
309 | #nsSslServerName
310 |
311 | # This really needs to be in place for it to be a proxy certificate.
312 | proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
313 |
314 | ####################################################################
315 | [ tsa ]
316 |
317 | default_tsa = tsa_config1 # the default TSA section
318 |
319 | [ tsa_config1 ]
320 |
321 | # These are used by the TSA reply generation only.
322 | dir = ./demoCA # TSA root directory
323 | serial = $dir/tsaserial # The current serial number (mandatory)
324 | crypto_device = builtin # OpenSSL engine to use for signing
325 | signer_cert = $dir/tsacert.pem # The TSA signing certificate
326 | # (optional)
327 | certs = $dir/cacert.pem # Certificate chain to include in reply
328 | # (optional)
329 | signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
330 | signer_digest = sha256 # Signing digest to use. (Optional)
331 | default_policy = tsa_policy1 # Policy if request did not specify it
332 | # (optional)
333 | other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
334 | digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory)
335 | accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
336 | clock_precision_digits = 0 # number of digits after dot. (optional)
337 | ordering = yes # Is ordering defined for timestamps?
338 | # (optional, default: no)
339 | tsa_name = yes # Must the TSA name be included in the reply?
340 | # (optional, default: no)
341 | ess_cert_id_chain = no # Must the ESS cert id chain be included?
342 | # (optional, default: no)
343 |
--------------------------------------------------------------------------------
/ztncui_code@0.8.6/src/controllers/networkController.js:
--------------------------------------------------------------------------------
1 | /*
2 | ztncui - ZeroTier network controller UI
3 | Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
4 | Licensed under GPLv3 - see LICENSE for details.
5 | */
6 |
7 | const fs = require('fs');
8 | const ipaddr = require('ip-address');
9 | const storage = require('node-persist');
10 | const zt = require('./zt');
11 | const util = require('util');
12 |
13 | storage.initSync({dir: 'etc/storage'});
14 |
15 | async function get_network_with_members(nwid) {
16 | const [network, peers, members] = await Promise.all([
17 | zt.network_detail(nwid),
18 | zt.peers(),
19 | zt.members(nwid)
20 | .then(member_ids =>
21 | Promise.all(
22 | Object.keys(member_ids)
23 | .map(id => Promise.all([
24 | zt.member_detail(nwid, id),
25 | storage.getItem(id)
26 | ]))
27 | )
28 | ).then(results => results.map(([member, name]) => {
29 | member.name = name || '';
30 | return member;
31 | }))
32 | ]);
33 | for (const member of members) {
34 | member.peer = peers.find(x => x.address === member.address);
35 | }
36 | return {network, members};
37 | }
38 |
39 | async function get_network_member(nwid, memberid) {
40 | const [network, member, peer, name] = await Promise.all([
41 | zt.network_detail(nwid),
42 | zt.member_detail(nwid, memberid),
43 | zt.peer(memberid),
44 | storage.getItem(memberid)
45 | ]);
46 | member.name = name || '';
47 | member.peer = peer;
48 | return {network, member};
49 | }
50 |
51 | // ZT network controller home page
52 | exports.index = async function(req, res) {
53 | const navigate =
54 | {
55 | active: 'controller_home',
56 | }
57 |
58 | try {
59 | const zt_status = await zt.get_zt_status();
60 | res.render('index', {title: 'ztncui', navigate: navigate, zt_status});
61 | } catch (err) {
62 | res.render('index', {title: 'ztncui',
63 | navigate: navigate, error: 'ERROR getting ZT status: ' + err});
64 | }
65 | };
66 |
67 | // Display list of all networks on this ZT network controller
68 | exports.network_list = async function(req, res) {
69 | const navigate =
70 | {
71 | active: 'networks',
72 | }
73 |
74 | try {
75 | networks = await zt.network_list();
76 | res.render('networks', {title: 'Networks on this controller', navigate: navigate, networks: networks});
77 | } catch (err) {
78 | res.render('networks', {title: 'Networks on this controller', navigate: navigate, error: 'Error retrieving list of networks on this controller: ' + err});
79 | }
80 | };
81 |
82 | // Display detail page for specific network
83 | exports.network_detail = async function(req, res) {
84 | const navigate =
85 | {
86 | active: 'networks',
87 | whence: '/controller/networks'
88 | }
89 |
90 | try {
91 | const [
92 | {network, members},
93 | zt_address
94 | ] = await Promise.all([
95 | get_network_with_members(req.params.nwid),
96 | zt.get_zt_address()
97 | ]);
98 | res.render('network_detail', {title: 'Network ' + network.name, navigate: navigate, network: network, members: members, zt_address: zt_address});
99 | } catch (err) {
100 | res.render('network_detail', {title: 'Detail for network', navigate: navigate, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err});
101 | }
102 | };
103 |
104 | // Display Network create form on GET
105 | exports.network_create_get = function(req, res) {
106 | const navigate =
107 | {
108 | active: 'add_network',
109 | }
110 |
111 | res.render('network_create', {title: 'Create network', navigate: navigate});
112 | };
113 |
114 | // Handle Network create on POST
115 | exports.network_create_post = async function(req, res) {
116 | const navigate =
117 | {
118 | active: 'add_network',
119 | }
120 |
121 | req.checkBody('name', 'Network name required').notEmpty();
122 |
123 | req.sanitize('name').escape();
124 | req.sanitize('name').trim();
125 |
126 | const errors = req.validationErrors();
127 |
128 | let name = { name: req.body.name };
129 |
130 | if (errors) {
131 | res.render('network_create', {title: 'Create Network', navigate: navigate, name: name, errors: errors});
132 | return;
133 | } else {
134 | try {
135 | const network = await zt.network_create(name);
136 | res.redirect('/controller/network/' + network.nwid);
137 | } catch (err) {
138 | res.render('network_detail', {title: 'Create Network - error', navigate: navigate, error: 'Error creating network ' + name.name});
139 | }
140 | }
141 | };
142 |
143 | // Display Network delete form on GET
144 | exports.network_delete_get = async function(req, res) {
145 | const navigate =
146 | {
147 | active: 'networks',
148 | whence: '/controller/networks'
149 | }
150 |
151 | try {
152 | const network = await zt.network_detail(req.params.nwid);
153 | res.render('network_delete', {title: 'Delete network', navigate: navigate,
154 | nwid: req.params.nwid, network: network});
155 | } catch (err) {
156 | res.render('network_delete', {title: 'Delete network', navigate: navigate, error: 'Error resolving network ' + req.params.nwid + ': ' + err});
157 | }
158 | };
159 |
160 | // Handle Network delete on POST
161 | exports.network_delete_post = async function(req, res) {
162 | const navigate =
163 | {
164 | active: 'networks',
165 | whence: '/controller/networks'
166 | }
167 |
168 | try {
169 | const network = await zt.network_delete(req.params.nwid);
170 | res.render('network_delete', {title: 'Delete network', navigate: navigate, network: network});
171 | } catch (err) {
172 | res.render('network_delete', {title: 'Delete network', navigate: navigate, error: 'Error deleting network ' + req.params.nwid + ': ' + err});
173 | }
174 | };
175 |
176 | // Network object GET
177 | exports.network_object = async function(req, res) {
178 | const navigate =
179 | {
180 | active: 'networks',
181 | whence: ''
182 | }
183 |
184 | try {
185 | const network = await zt.network_detail(req.params.nwid);
186 | navigate.whence = '/controller/network/' + network.nwid;
187 | res.render(req.params.object, {title: req.params.object, navigate: navigate, network: network}, function(err, html) {
188 | if (err) {
189 | if (err.message.indexOf('Failed to lookup view') !== -1 ) {
190 | return res.render('not_implemented', {title: req.params.object, navigate: navigate, network: network});
191 | }
192 | throw err;
193 | }
194 | res.send(html);
195 | });
196 | } catch (err) {
197 | res.render(req.params.object, {title: req.params.object, navigate: navigate, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err});
198 | }
199 | }
200 |
201 | // Handle Network rename form on POST
202 | exports.name = async function(req, res) {
203 | const navigate =
204 | {
205 | active: 'networks',
206 | whence: '/controller/networks'
207 | }
208 |
209 | req.checkBody('name', 'Network name required').notEmpty();
210 | req.sanitize('name').escape();
211 | req.sanitize('name').trim();
212 |
213 | const errors = req.validationErrors();
214 |
215 | let name = { name: req.body.name };
216 |
217 | if (errors) {
218 | console.error("network name validation errors", errors);
219 | } else {
220 | try {
221 | const network = await zt.network_object(req.params.nwid, name);
222 | } catch ( err) {
223 | console.error("Error renaming network " + req.params.nwid, err);
224 | }
225 | }
226 | res.redirect('/controller/network/' + req.params.nwid);
227 | };
228 |
229 | // ipAssignmentPools POST
230 | exports.ipAssignmentPools = async function(req, res) {
231 | const navigate =
232 | {
233 | active: 'networks',
234 | whence: ''
235 | }
236 |
237 | req.checkBody('ipRangeStart', 'IP range start required').notEmpty();
238 | req.checkBody('ipRangeStart', 'IP range start needs a valid IPv4 or IPv6 address').isIP();
239 | req.sanitize('ipRangeStart').escape();
240 | req.sanitize('ipRangeStart').trim();
241 | req.checkBody('ipRangeEnd', 'IP range end required').notEmpty();
242 | req.checkBody('ipRangeEnd', 'IP range end needs a valid IPv4 or IPv6 address').isIP();
243 | req.sanitize('ipRangEnd').escape();
244 | req.sanitize('ipRangEnd').trim();
245 |
246 | const errors = req.validationErrors();
247 |
248 | const ipAssignmentPool =
249 | {
250 | ipRangeStart: req.body.ipRangeStart,
251 | ipRangeEnd: req.body.ipRangeEnd
252 | };
253 |
254 | if (errors) {
255 | try {
256 | const network = await zt.network_detail(req.params.nwid);
257 | navigate.whence = '/controller/network/' + network.nwid;
258 | res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, ipAssignmentPool: ipAssignmentPool, network: network, errors: errors});
259 | } catch (err) {
260 | res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, error: 'Error resolving network detail for network ' + req.params.nwid + ': ' + err});
261 | }
262 | } else {
263 | try {
264 | const network = await zt.ipAssignmentPools(req.params.nwid, ipAssignmentPool, 'add');
265 | navigate.whence = '/controller/network/' + network.nwid;
266 | res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, ipAssignmentPool: ipAssignmentPool, network: network});
267 | } catch (err) {
268 | res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, error: 'Error applying IP Assignment Pools for network ' + req.params.nwid + ': ' + err});
269 | }
270 | }
271 | }
272 |
273 | isValidPrefix = function(str, max) {
274 | const num = Math.floor(Number(str));
275 | return String(num) == str && num >= 0 && num <= max;
276 | }
277 |
278 | // routes POST
279 | exports.routes = async function (req, res) {
280 | const navigate =
281 | {
282 | active: 'networks',
283 | whence: ''
284 | }
285 |
286 | req.checkBody('target', 'Target network is required').notEmpty();
287 | req.sanitize('target').trim();
288 | req.checkBody('target', 'Target network must be valid CIDR format')
289 | .custom(value => {
290 | const parts = value.split('/');
291 | const ipv4 = new ipaddr.Address4(parts[0]);
292 | const ipv6 = new ipaddr.Address6(parts[0]);
293 | let isValidIPv4orIPv6 = false;
294 | let prefixMax = 32;
295 | if (ipv4.isValid()) {
296 | isValidIPv4orIPv6 = true;
297 | } else {
298 | }
299 | if (ipv6.isValid()) {
300 | isValidIPv4orIPv6 = true;
301 | prefixMax = 128;
302 | } else {
303 | }
304 | return isValidIPv4orIPv6 && isValidPrefix(parts[1], prefixMax);
305 | });
306 | req.checkBody('via', 'Gateway must be a valid IPv4 or IPv6 address').optional({ checkFalsy: true }).isIP();
307 | req.sanitize('via').escape();
308 | req.sanitize('via').trim();
309 | if (! req.body.via) {
310 | req.body.via = null;
311 | }
312 |
313 | const errors = req.validationErrors();
314 |
315 | const route =
316 | {
317 | target: req.body.target,
318 | via: req.body.via
319 | };
320 |
321 | if (errors) {
322 | try {
323 | const network = await zt.network_detail(req.params.nwid);
324 | navigate.whence = '/controller/network/' + network.nwid;
325 | res.render('routes', {title: 'routes', navigate: navigate, route: route, network: network, errors: errors});
326 | } catch (err) {
327 | res.render('routes', {title: 'routes', navigate: navigate, error: 'Error resolving network detail'});
328 | }
329 | } else {
330 | try {
331 | const network = await zt.routes(req.params.nwid, route, 'add');
332 | navigate.whence = '/controller/network/' + network.nwid;
333 | res.render('routes', {title: 'routes', navigate: navigate, route: route, network: network});
334 | } catch (err) {
335 | res.render('routes', {title: 'routes', navigate: navigate, error: 'Error adding route for network ' + req.params.nwid + ': ' + err});
336 | }
337 | }
338 |
339 | }
340 |
341 | // route_delete GET
342 | exports.route_delete = async function (req, res) {
343 | const navigate =
344 | {
345 | active: 'networks',
346 | whence: ''
347 | }
348 |
349 | const route =
350 | {
351 | target: req.params.target_ip + '/' + req.params.target_prefix,
352 | via: null
353 | };
354 |
355 |
356 | try {
357 | const network = await zt.routes(req.params.nwid, route, 'delete');
358 | navigate.whence = '/controller/network/' + network.nwid;
359 | res.render('routes', {title: 'routes', navigate: navigate, route: route, network: network});
360 | } catch (err) {
361 | res.render('routes', {title: 'routes', navigate: navigate, error: 'Error deleting route for network ' + req.params.nwid + ': ' + err});
362 | }
363 | }
364 |
365 | // ipAssignmentPool_delete GET
366 | exports.ipAssignmentPool_delete = async function (req, res) {
367 | const navigate =
368 | {
369 | active: 'networks',
370 | whence: ''
371 | }
372 |
373 | const ipAssignmentPool =
374 | {
375 | ipRangeStart: req.params.ipRangeStart,
376 | ipRangeEnd: req.params.ipRangeEnd
377 | };
378 |
379 |
380 | try {
381 | const network = await zt.ipAssignmentPools(req.params.nwid, ipAssignmentPool, 'delete');
382 | navigate.whence = '/controller/network/' + network.nwid;
383 | res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, ipAssignmentPool: ipAssignmentPool, network: network});
384 | } catch (err) {
385 | res.render('ipAssignmentPools', {title: 'ipAssignmentPools', navigate: navigate, error: 'Error deleting IP Assignment Pool for network ' + req.params.nwid + ': ' + err});
386 | }
387 | }
388 |
389 | // private POST
390 | exports.private = async function (req, res) {
391 | const navigate =
392 | {
393 | active: 'networks',
394 | whence: ''
395 | }
396 |
397 | const private =
398 | {
399 | private: req.body.private
400 | };
401 |
402 | try {
403 | const network = await zt.network_object(req.params.nwid, private);
404 | navigate.whence = '/controller/network/' + network.nwid;
405 | res.render('private', {title: 'private', navigate: navigate, network: network});
406 | } catch (err) {
407 | res.render('private', {title: 'private', navigate: navigate, error: 'Error applying private for network ' + req.params.nwid + ': ' + err});
408 | }
409 | }
410 |
411 | // v4AssignMode POST
412 | exports.v4AssignMode = async function (req, res) {
413 | const navigate =
414 | {
415 | active: 'networks',
416 | whence: ''
417 | }
418 |
419 | const v4AssignMode =
420 | {
421 | v4AssignMode: { zt: req.body.zt }
422 | };
423 |
424 | try {
425 | const network = await zt.network_object(req.params.nwid, v4AssignMode);
426 | navigate.whence = '/controller/network/' + network.nwid;
427 | res.render('v4AssignMode', {title: 'v4AssignMode', navigate: navigate, network: network});
428 | } catch (err) {
429 | res.render('v4AssignMode', {title: 'v4AssignMode', navigate: navigate, error: 'Error applying v4AssignMode for network ' + req.params.nwid + ': ' + err});
430 | }
431 | }
432 |
433 | // v6AssignMode POST
434 | exports.v6AssignMode = async function (req, res) {
435 | const navigate =
436 | {
437 | active: 'networks',
438 | whence: ''
439 | }
440 |
441 | const v6AssignMode =
442 | {
443 | v6AssignMode:
444 | {
445 | '6plane': req.body['6plane'],
446 | 'rfc4193': req.body.rfc4193,
447 | 'zt': req.body.zt
448 | }
449 | };
450 |
451 | try {
452 | const network = await zt.network_object(req.params.nwid, v6AssignMode);
453 | navigate.whence = '/controller/network/' + network.nwid;
454 | res.render('v6AssignMode', {title: 'v6AssignMode', navigate: navigate, network: network});
455 | } catch (err) {
456 | res.render('v6AssignMode', {title: 'v6AssignMode', navigate: navigate, error: 'Error applying v6AssignMode for network ' + req.params.nwid + ': ' + err});
457 | }
458 | }
459 |
460 | // dns POST
461 | exports.dns = async function (req, res) {
462 | const navigate = {
463 | active: 'networks',
464 | whence: ''
465 | };
466 |
467 | const dns = {
468 | dns: {
469 | domain: req.body.domain,
470 | servers: req.body.servers
471 | .split('\n')
472 | .map(x => x.trim())
473 | .filter(ip =>
474 | new ipaddr.Address4(ip).isValid() ||
475 | new ipaddr.Address6(ip).isValid()
476 | )
477 | }
478 | };
479 |
480 | try {
481 | const network = await zt.network_object(req.params.nwid, dns);
482 | navigate.whence = '/controller/network/' + network.nwid;
483 | res.render('dns', {title: 'dns', navigate: navigate, network: network});
484 | } catch (err) {
485 | res.render('dns', {title: 'dns', navigate: navigate, error: 'Error updating dns for network ' + req.params.nwid + ': ' + err});
486 | }
487 | }
488 |
489 | // Display detail page for specific member
490 | exports.member_detail = async function(req, res) {
491 | const navigate =
492 | {
493 | active: 'networks',
494 | whence: ''
495 | }
496 |
497 | try {
498 | const {network, member} = await get_network_member(req.params.nwid, req.params.id);
499 | navigate.whence = '/controller/network/' + network.nwid + '#members';
500 | res.render('member_detail', {title: 'Network member detail', navigate: navigate, network: network, member: member});
501 | } catch (err) {
502 | console.error(err);
503 | res.render('error', {title: req.params.object, navigate: navigate, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err});
504 | }
505 | };
506 |
507 | // Member object GET
508 | exports.member_object = async function(req, res) {
509 | const navigate =
510 | {
511 | active: 'networks',
512 | whence: ''
513 | }
514 |
515 | try {
516 | const {network, member} = await get_network_member(req.params.nwid, req.params.id);
517 | navigate.whence = '/controller/network/' + network.nwid + '#members';
518 | res.render(req.params.object, {title: req.params.object, navigate: navigate, network: network, member: member}, function(err, html) {
519 | if (err) {
520 | if (err.message.indexOf('Failed to lookup view') !== -1 ) {
521 | return res.render('not_implemented', {title: req.params.object, navigate: navigate, network: network, member: member});
522 | }
523 | throw err;
524 | }
525 | res.send(html);
526 | });
527 | } catch (err) {
528 | res.render(req.params.object, {title: req.params.object, navigate: navigate, error: 'Error resolving detail for member ' + req.params.id + ' of network ' + req.params.nwid + ': ' + err});
529 | }
530 | }
531 |
532 | // Easy network setup GET
533 | exports.easy_get = async function(req, res) {
534 | const navigate =
535 | {
536 | active: 'networks',
537 | whence: '/controller/network/' + req.params.nwid
538 | }
539 |
540 | try {
541 | const network = await zt.network_detail(req.params.nwid);
542 | res.render('network_easy', {title: 'Easy setup of network', navigate: navigate, network: network});
543 | } catch (err) {
544 | res.render('network_easy', {title: 'Easy setup of network', navigate: navigate, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err});
545 | }
546 | }
547 |
548 | // Easy network setup POST
549 | exports.easy_post = async function(req, res) {
550 | const navigate =
551 | {
552 | active: 'networks',
553 | whence: '/controller/networks'
554 | }
555 |
556 | req.checkBody('networkCIDR', 'Network address is required').notEmpty();
557 | req.sanitize('networkCIDR').trim();
558 | req.checkBody('networkCIDR', 'Network address must be in CIDR notation')
559 | .custom(value => {
560 | const parts = value.split('/');
561 | const ipv4 = new ipaddr.Address4(parts[0]);
562 | return ipv4.isValid() && isValidPrefix(parts[1], 32);
563 | });
564 | req.checkBody('poolStart', 'Start of IP assignment pool is required')
565 | .notEmpty();
566 | req.checkBody('poolStart', 'Start of IP assignment pool must be valid IPv4 address')
567 | .isIP(4);
568 | req.sanitize('poolStart').escape();
569 | req.sanitize('poolStart').trim();
570 | req.checkBody('poolEnd', 'End of IP assignment pool is required')
571 | .notEmpty();
572 | req.checkBody('poolEnd', 'End of IP assignment pool must be valid IPv4 address')
573 | .isIP(4);
574 | req.sanitize('poolEnd').escape();
575 | req.sanitize('poolEnd').trim();
576 |
577 | const errors = req.validationErrors();
578 |
579 | const ipAssignmentPools =
580 | [{
581 | ipRangeStart: req.body.poolStart,
582 | ipRangeEnd: req.body.poolEnd
583 | }];
584 |
585 | const routes =
586 | [{
587 | target: req.body.networkCIDR,
588 | via: null
589 | }];
590 |
591 | const v4AssignMode =
592 | {
593 | zt: true
594 | };
595 |
596 | if (errors) {
597 | network =
598 | {
599 | ipAssignmentPools: ipAssignmentPools,
600 | routes: routes,
601 | v4AssignMode: v4AssignMode
602 | };
603 |
604 | res.render('network_easy', {title: 'Easy setup of network', navigate: navigate, network: network, errors: errors});
605 | } else {
606 | try {
607 | const network = await zt.network_easy_setup(req.params.nwid,
608 | routes,
609 | ipAssignmentPools,
610 | v4AssignMode);
611 | res.render('network_easy', {title: 'Easy setup of network', navigate: navigate, network: network, message: 'Network setup succeeded'});
612 | } catch (err) {
613 | res.render('network_easy', {title: 'Easy setup of network', navigate: navigate, error: 'Error resolving detail for network ' + req.params.nwid + ': ' + err});
614 | }
615 | }
616 | }
617 |
618 | // Easy members auth POST
619 | exports.members = async function(req, res) {
620 | const navigate =
621 | {
622 | active: 'networks',
623 | whence: '/controller/networks'
624 | }
625 |
626 | let errors = null;
627 |
628 | if (req.method === 'POST') {
629 |
630 | req.checkBody('id', 'Member ID is required').notEmpty();
631 | req.sanitize('id').trim();
632 | req.sanitize('id').escape();
633 |
634 | if (req.body.auth) {
635 | req.checkBody('auth', 'Authorization state must be boolean').isBoolean();
636 | req.sanitize('auth').trim();
637 | req.sanitize('auth').escape();
638 |
639 | errors = req.validationErrors();
640 |
641 | if (!errors) {
642 | const auth =
643 | {
644 | authorized: req.body.auth
645 | };
646 |
647 | try {
648 | const mem = await zt.member_object(req.params.nwid, req.body.id, auth);
649 | } catch (err) {
650 | throw err;
651 | }
652 | }
653 | } else if (req.body.activeBridge) {
654 | req.checkBody('activeBridge', 'activeBridge state must be boolean').isBoolean();
655 | req.sanitize('activeBridge').trim();
656 | req.sanitize('activeBridge').escape();
657 |
658 | errors = req.validationErrors();
659 |
660 | if (!errors) {
661 | const activeBridge =
662 | {
663 | activeBridge: req.body.activeBridge
664 | };
665 |
666 | try {
667 | const mem = await zt.member_object(req.params.nwid, req.body.id, activeBridge);
668 | } catch (err) {
669 | throw err;
670 | }
671 | }
672 | } else if (req.body.name) {
673 | req.sanitize('name').trim();
674 | req.sanitize('name').escape();
675 |
676 | errors = req.validationErrors();
677 |
678 | if (!errors) {
679 | try {
680 | const ret = await storage.setItem(req.body.id, req.body.name);
681 | } catch (err) {
682 | throw err;
683 | }
684 | }
685 | }
686 | } else { // GET
687 | res.redirect("/controller/network/" + req.params.nwid + "#members");
688 | }
689 | }
690 |
691 | // Member delete GET or POST
692 | exports.member_delete = async function(req, res) {
693 | const navigate =
694 | {
695 | active: 'networks',
696 | whence: ''
697 | }
698 |
699 | try {
700 | const network = await zt.network_detail(req.params.nwid);
701 | let member = null;
702 | let name = null;
703 | if (req.method === 'POST') {
704 | member = await zt.member_delete(req.params.nwid, req.params.id);
705 | if (member.deleted) {
706 | name = await storage.removeItem(member.id);
707 | }
708 | } else {
709 | member = await zt.member_detail(req.params.nwid, req.params.id);
710 | name = await storage.getItem(member.id);
711 | }
712 | member.name = name || '';
713 |
714 | navigate.whence = '/controller/network/' + network.nwid;
715 | res.render('member_delete', {title: 'Delete member from ' + network.name,
716 | navigate: navigate, network: network, member: member});
717 | } catch (err) {
718 | res.render('member_delete', {title: 'Delete member from network', navigate: navigate,
719 | error: 'Error resolving detail for member ' + req.params.id
720 | + ' of network ' + req.params.nwid + ': ' + err});
721 | }
722 | }
723 |
724 | // ipAssignment delete GET
725 | exports.delete_ip = async function(req, res) {
726 | const navigate =
727 | {
728 | active: 'networks',
729 | whence: ''
730 | }
731 |
732 | try {
733 | const network = await zt.network_detail(req.params.nwid);
734 | let member = await zt.member_detail(req.params.nwid, req.params.id);
735 | navigate.whence = '/controller/network/' + network.nwid;
736 | member.name = await storage.getItem(member.id) | '';
737 | if (req.params.index) {
738 | member = await zt.ipAssignmentDelete(network.nwid, member.id,
739 | req.params.index);
740 | res.redirect('/controller/network/' + network.nwid + '/member/' +
741 | member.id + '/ipAssignments');
742 | }
743 | res.render('ipAssignments', {title: 'ipAssignments ' + network.name,
744 | navigate: navigate, index: req.params.index, network: network, member: member});
745 | } catch (err) {
746 | res.render('ipAssignments', {title: 'ipAssignments', navigate: navigate,
747 | error: 'Error resolving detail for member ' + req.params.id
748 | + ' of network ' + req.params.nwid + ': ' + err});
749 | }
750 | }
751 |
752 | // ipAssignments POST
753 | exports.assign_ip = async function(req, res) {
754 | const navigate =
755 | {
756 | active: 'networks',
757 | whence: ''
758 | }
759 |
760 | try {
761 | var network = await zt.network_detail(req.params.nwid);
762 | } catch (err) {
763 | throw err;
764 | }
765 |
766 | req.checkBody('ipAddress', 'IP address required').notEmpty();
767 | req.checkBody('ipAddress', 'IP address must be a valid IPv4 or IPv6 address').isIP();
768 | req.checkBody('ipAddress', 'IP address must fall within a managed route')
769 | .custom(value => {
770 | let ipAddressInManagedRoute = false;
771 | network.routes.forEach(function(item) {
772 | let ipv4 = new ipaddr.Address4(value);
773 | let target4 = new ipaddr.Address4(item.target);
774 | if (ipv4.isValid() && target4.isValid()) {
775 | if (ipv4.isInSubnet(target4)) ipAddressInManagedRoute = true;
776 | }
777 | let ipv6 = new ipaddr.Address6(value);
778 | let target6 = new ipaddr.Address6(item.target);
779 | if (ipv6.isValid() && target6.isValid()) {
780 | if (ipv6.isInSubnet(target6)) ipAddressInManagedRoute = true;
781 | }
782 | });
783 | return ipAddressInManagedRoute;
784 | });
785 | req.sanitize('ipAddress').escape();
786 | req.sanitize('ipAddress').trim();
787 |
788 | const errors = req.validationErrors();
789 |
790 | const ipAssignment = { ipAddress: req.body.ipAddress };
791 |
792 | try {
793 | let member = await zt.member_detail(req.params.nwid, req.params.id);
794 | navigate.whence = '/controller/network/' + network.nwid;
795 |
796 | if (!errors) {
797 | member = await zt.ipAssignmentAdd(network.nwid, member.id, ipAssignment);
798 | }
799 |
800 | member.name = await storage.getItem(member.id) | '';
801 |
802 | res.render('ipAssignments', {title: 'ipAssignments', navigate: navigate,
803 | ipAssignment: ipAssignment, network: network, member: member,
804 | errors: errors});
805 | } catch (err) {
806 | res.render('ipAssignments', {title: 'ipAssignments', navigate: navigate,
807 | error: 'Error resolving detail for member ' + req.params.id
808 | + ' of network ' + req.params.nwid + ': ' + err});
809 | }
810 | }
811 |
--------------------------------------------------------------------------------