Compare commits
12 Commits
IPv6_route
...
master
Author | SHA1 | Date | |
---|---|---|---|
d982e4e038 | |||
533a14548a | |||
4f02ee057f | |||
b7f4c81f55 | |||
024ab9d116 | |||
88c8f68260 | |||
|
1b2284864d | ||
|
42248c1359 | ||
|
0557b386a9 | ||
|
794f846125 | ||
|
8fb29fa607 | ||
|
b50215f620 |
37
README.md
37
README.md
@ -10,7 +10,7 @@ Follow us on [![alt @key_networks on Twitter](https://i.imgur.com/wWzX9uB.png)](
|
|||||||
Instructions for installing on Linux from RPM or DEB packges are available at [key-networks.com/ztncui](https://key-networks.com/ztncui).
|
Instructions for installing on Linux from RPM or DEB packges are available at [key-networks.com/ztncui](https://key-networks.com/ztncui).
|
||||||
|
|
||||||
## Docker Container Image
|
## Docker Container Image
|
||||||
See [github.com/key-networks/ztncui-containerized](https://github.com/key-networks/ztncui-containerized)
|
See [https://github.com/key-networks/ztncui-aio](https://github.com/key-networks/ztncui-aio)
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@ -45,21 +45,9 @@ npm install
|
|||||||
|
|
||||||
##### 3. authtoken.secret
|
##### 3. authtoken.secret
|
||||||
|
|
||||||
The app needs to know the zerotier-one authtoken.secret. There are two options:
|
The app needs to know the zerotier-one authtoken.secret.
|
||||||
|
|
||||||
###### A: Allow access to /var/lib/zerotier-one/authtoken.secret
|
###### Make a .env file
|
||||||
The user running the ztncui app needs read access to authtoken.secret. This can be achieved with:
|
|
||||||
```shell
|
|
||||||
sudo usermod -aG zerotier-one username
|
|
||||||
sudo chmod g+r /var/lib/zerotier-one/authtoken.secret
|
|
||||||
```
|
|
||||||
Where:
|
|
||||||
* username is the user running the ztncui app
|
|
||||||
|
|
||||||
Note that you need to log out and in again to apply the new group membership.
|
|
||||||
|
|
||||||
###### OR
|
|
||||||
###### B: Make a .env file
|
|
||||||
In the root of the ztncui directory, create a `.env` file with the content:
|
In the root of the ztncui directory, create a `.env` file with the content:
|
||||||
```
|
```
|
||||||
ZT_TOKEN=########################
|
ZT_TOKEN=########################
|
||||||
@ -67,17 +55,19 @@ ZT_TOKEN=########################
|
|||||||
Where:
|
Where:
|
||||||
* ######################## is the token string.
|
* ######################## is the token string.
|
||||||
|
|
||||||
You can also specify in the `.env` file a different address for the zerotier-one API (which defaults to localhost:9993):
|
After all edits to the `.env file` (see other options below), make the `.env` readable by the user running ztncui only:
|
||||||
|
```shell
|
||||||
|
chmod 400 .env
|
||||||
|
chown ztncui.ztncui .env
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 4. Zerotier-one API port
|
||||||
|
|
||||||
|
You can specify in the `.env` file a different address for the zerotier-one API (which defaults to localhost:9993):
|
||||||
```
|
```
|
||||||
ZT_ADDR=localhost:9995
|
ZT_ADDR=localhost:9995
|
||||||
```
|
```
|
||||||
|
|
||||||
Make `.env` readable by the user running ztncui only:
|
|
||||||
```shell
|
|
||||||
chmod 600 .env
|
|
||||||
```
|
|
||||||
|
|
||||||
The `.env` file should make it easier to run ztncui on a non-Linux platform.
|
|
||||||
|
|
||||||
##### 4. Run in production mode
|
##### 4. Run in production mode
|
||||||
To run the server in production mode, add the following to the `.env` file (see 3B above):
|
To run the server in production mode, add the following to the `.env` file (see 3B above):
|
||||||
@ -315,4 +305,5 @@ Problems with ztncui can be reported using the GitHub issue tracking system. Pl
|
|||||||
The ztncui code is open source code, licensed under the GNU GPLv3, and is free to use on those terms. If you are interested in commercial licensing, please contact us via the contact form at [key-networks.com](https://key-networks.com) .
|
The ztncui code is open source code, licensed under the GNU GPLv3, and is free to use on those terms. If you are interested in commercial licensing, please contact us via the contact form at [key-networks.com](https://key-networks.com) .
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
@lideming for a rework and improvement of the network details page, adding DNS support, peer status/address/latency and other improvements.
|
- @lideming for a rework and improvement of the network details page, adding DNS support, peer status/address/latency and other improvements.
|
||||||
|
- @Koromix for a fix for incompatibility with ZeroTier 1.12.
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
getent passwd ztncui || useradd --system --home-dir /opt/key-networks/ztncui --shell /bin/false ztncui
|
||||||
|
if [ $(getent group zerotier-one) ]; then
|
||||||
|
echo "Adding user ztncui to group zerotier-one..."
|
||||||
|
usermod -a -G zerotier-one ztncui
|
||||||
|
chmod g+r /var/lib/zerotier-one/authtoken.secret
|
||||||
|
else
|
||||||
|
echo "Could not add user ztncui to group zerotier-one... is zerotier-one installed?"
|
||||||
|
fi
|
@ -4,7 +4,7 @@
|
|||||||
],
|
],
|
||||||
"cflags+": ["-Wno-cast-function-type"],
|
"cflags+": ["-Wno-cast-function-type"],
|
||||||
"include_dirs+": ["<!(node -e \"require('nan')\")"],
|
"include_dirs+": ["<!(node -e \"require('nan')\")"],
|
||||||
+ "libraries": ["/usr/lib/gcc/x86_64-redhat-linux/8/libstdc++.a"],
|
+ "libraries": ["/usr/lib/gcc/x86_64-redhat-linux/8/libstdc++fs.a"],
|
||||||
"dependencies": ["libargon2"],
|
"dependencies": ["libargon2"],
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"Debug": {
|
"Debug": {
|
||||||
|
@ -26,7 +26,7 @@ BINDINGGYP='node_modules/argon2/binding.gyp'
|
|||||||
|
|
||||||
NODE_VER='v16'
|
NODE_VER='v16'
|
||||||
|
|
||||||
if [ ! -f /usr/lib/gcc/x86_64-redhat-linux/8/libstdc++.a ]; then
|
if [ ! -f /usr/lib/gcc/x86_64-redhat-linux/8/libstdc++fs.a ]; then
|
||||||
echo "You must install libstdc++-static"
|
echo "You must install libstdc++-static"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -17,15 +17,26 @@ async function get_network_with_members(nwid) {
|
|||||||
zt.network_detail(nwid),
|
zt.network_detail(nwid),
|
||||||
zt.peers(),
|
zt.peers(),
|
||||||
zt.members(nwid)
|
zt.members(nwid)
|
||||||
.then(member_ids =>
|
.then(member_ids => {
|
||||||
Promise.all(
|
// Fix weird data returned by ZeroTier 1.12
|
||||||
|
if (Array.isArray(member_ids)) {
|
||||||
|
let obj = {};
|
||||||
|
for (let id of member_ids) {
|
||||||
|
let key = Object.keys(id)[0];
|
||||||
|
let value = Object.values(id)[0];
|
||||||
|
obj[key] = value;
|
||||||
|
}
|
||||||
|
member_ids = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
Object.keys(member_ids)
|
Object.keys(member_ids)
|
||||||
.map(id => Promise.all([
|
.map(id => Promise.all([
|
||||||
zt.member_detail(nwid, id),
|
zt.member_detail(nwid, id),
|
||||||
storage.getItem(id)
|
storage.getItem(id)
|
||||||
]))
|
]))
|
||||||
)
|
);
|
||||||
).then(results => results.map(([member, name]) => {
|
}).then(results => results.map(([member, name]) => {
|
||||||
member.name = name || '';
|
member.name = name || '';
|
||||||
return member;
|
return member;
|
||||||
}))
|
}))
|
||||||
|
@ -186,13 +186,9 @@ exports.routes = async function(nwid, route, action) {
|
|||||||
|
|
||||||
const network = await network_detail(nwid);
|
const network = await network_detail(nwid);
|
||||||
let routes = network.routes;
|
let routes = network.routes;
|
||||||
const target6 = new ipaddr.Address6(route.target);
|
route.target = canonicalTarget(route.target);
|
||||||
if (target6.isValid()) {
|
|
||||||
const parts = route.target.split('/');
|
|
||||||
route.target = target6.canonicalForm() + '/' + parts[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
const route_to_del = routes.find(rt => rt.target === route.target);
|
const route_to_del = routes.find(rt => canonicalTarget(rt.target) === route.target);
|
||||||
|
|
||||||
if (!route_to_del) {
|
if (!route_to_del) {
|
||||||
if (action === 'add') {
|
if (action === 'add') {
|
||||||
@ -220,6 +216,15 @@ exports.routes = async function(nwid, route, action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canonicalTarget(target) {
|
||||||
|
const target6 = new ipaddr.Address6(target);
|
||||||
|
if (target6.isValid()) {
|
||||||
|
const parts = target.split('/');
|
||||||
|
return target6.canonicalForm() + '/' + parts[1];
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
exports.network_object = async function(nwid, object) {
|
exports.network_object = async function(nwid, object) {
|
||||||
const options = await init_options();
|
const options = await init_options();
|
||||||
options.method = 'POST';
|
options.method = 'POST';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ztncui",
|
"name": "ztncui",
|
||||||
"version": "0.8.4",
|
"version": "0.8.14",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./bin/www",
|
"start": "node ./bin/www",
|
||||||
@ -19,7 +19,7 @@
|
|||||||
"got": "^7.1.0",
|
"got": "^7.1.0",
|
||||||
"helmet": "^3.23.0",
|
"helmet": "^3.23.0",
|
||||||
"ip-address": "^5.8.9",
|
"ip-address": "^5.8.9",
|
||||||
"jquery": "~3.4.1",
|
"jquery": "^3.6.1",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
"node-persist": "^2.1.0",
|
"node-persist": "^2.1.0",
|
||||||
"pug": "^3.0.2",
|
"pug": "^3.0.2",
|
||||||
|
@ -21,7 +21,7 @@ function guest_only(req, res, next) {
|
|||||||
|
|
||||||
/* GET home page. */
|
/* GET home page. */
|
||||||
router.get('/', guest_only, function(req, res, next) {
|
router.get('/', guest_only, function(req, res, next) {
|
||||||
res.render('front_door', {title: 'ztncui'});
|
res.render('front_door', {title: '经纬飞行网络'});
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/logout', function(req, res) {
|
router.get('/logout', function(req, res) {
|
||||||
@ -33,13 +33,13 @@ router.get('/logout', function(req, res) {
|
|||||||
router.get('/login', guest_only, function(req, res) {
|
router.get('/login', guest_only, function(req, res) {
|
||||||
let message = null;
|
let message = null;
|
||||||
if (req.session.error) {
|
if (req.session.error) {
|
||||||
if (req.session.error !== 'Access denied!') {
|
if (req.session.error !== '无权限!') {
|
||||||
message = req.session.error;
|
message = req.session.error;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message = req.session.success;
|
message = req.session.success;
|
||||||
}
|
}
|
||||||
res.render('login', { title: 'Login', message: message });
|
res.render('login', { title: '登录', message: message });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/login', async function(req, res) {
|
router.post('/login', async function(req, res) {
|
||||||
@ -55,7 +55,7 @@ router.post('/login', async function(req, res) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
req.session.error = 'Authentication failed, please check your username and password.'
|
req.session.error = '登录失败,请检查用户名和密码.'
|
||||||
res.redirect('/login');
|
res.redirect('/login');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,5 +9,5 @@ block login_content
|
|||||||
h1!= title
|
h1!= title
|
||||||
|
|
||||||
h2
|
h2
|
||||||
a(href='https://zerotier.com' target='_blank') ZeroTier
|
a(href='https://www.jingweiht.com/zerotier' target='_blank') ZeroTier
|
||||||
| network controller UI
|
| 网络控制器
|
||||||
|
@ -31,9 +31,9 @@ html(lang='en')
|
|||||||
span.icon-bar
|
span.icon-bar
|
||||||
span.icon-bar
|
span.icon-bar
|
||||||
span.icon-bar
|
span.icon-bar
|
||||||
a.navbar-brand(href='https://key-networks.com' target='_blank')
|
a.navbar-brand(href='https://www.jingweiht.com' target='_blank')
|
||||||
img(src='/images/key-logo.svg' alt='Key Networks logo' height='25px' width='25px' style='display: inline')
|
img(src='/images/key-logo.svg' alt='Key Networks logo' height='25px' width='25px' style='display: inline')
|
||||||
| Key Networks
|
| 飞行网络
|
||||||
.collapse.navbar-collapse(id='BarNav')
|
.collapse.navbar-collapse(id='BarNav')
|
||||||
ul.nav.navbar-nav
|
ul.nav.navbar-nav
|
||||||
block nav_items
|
block nav_items
|
||||||
@ -42,5 +42,5 @@ html(lang='en')
|
|||||||
block nav_login
|
block nav_login
|
||||||
a(href='/logout')
|
a(href='/logout')
|
||||||
span.glyphicon.glyphicon-log-out
|
span.glyphicon.glyphicon-log-out
|
||||||
| Logout
|
| 退出
|
||||||
block body_content
|
block body_content
|
||||||
|
@ -9,14 +9,14 @@ block content
|
|||||||
h1!= title
|
h1!= title
|
||||||
|
|
||||||
h2
|
h2
|
||||||
a(href='https://zerotier.com' target='_blank') ZeroTier
|
a(href='https://www.jingweiht.com/zerotier' target='_blank') ZeroTier
|
||||||
| network controller UI by
|
| 飞行网络控制器
|
||||||
a(href='https://key-networks.com' target='_blank') Key Networks
|
a(href='https://www.jingweiht.com/key-networks' target='_blank') Key Networks
|
||||||
|
|
||||||
if error
|
if error
|
||||||
b #{error}
|
b #{error}
|
||||||
else
|
else
|
||||||
h4 This network controller has a ZeroTier address of <b>#{zt_status.address}</b>
|
h4 ZeroTier 网络地址 <b>#{zt_status.address}</b>
|
||||||
h4 ZeroTier version <b>#{zt_status.version}</b>
|
h4 ZeroTier 版本 <b>#{zt_status.version}</b>
|
||||||
h4
|
h4
|
||||||
a(href='/controller/networks') List all networks on this network controller
|
a(href='/controller/networks') 列表当前控制器下的所有网络
|
||||||
|
@ -21,7 +21,7 @@ block login_content
|
|||||||
form.form-horizontal(method='POST' action='')
|
form.form-horizontal(method='POST' action='')
|
||||||
.form-group.row
|
.form-group.row
|
||||||
.col-sm-2
|
.col-sm-2
|
||||||
label.control-label(for='username') Username:
|
label.control-label(for='username') 用户名:
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
.input-group
|
.input-group
|
||||||
span.input-group-addon
|
span.input-group-addon
|
||||||
@ -30,7 +30,7 @@ block login_content
|
|||||||
|
|
||||||
.form-group.row
|
.form-group.row
|
||||||
.col-sm-2
|
.col-sm-2
|
||||||
label.control-label(for='password') Password:
|
label.control-label(for='password') 密码:
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
.input-group
|
.input-group
|
||||||
span.input-group-addon
|
span.input-group-addon
|
||||||
@ -40,6 +40,6 @@ block login_content
|
|||||||
.form-group.row
|
.form-group.row
|
||||||
.col-sm-2
|
.col-sm-2
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
button.btn.btn-primary(type='submit') Login
|
button.btn.btn-primary(type='submit') 登录
|
||||||
= ' '
|
= ' '
|
||||||
a.btn.btn-default(href='/' name='cancel' role='button') Cancel
|
a.btn.btn-default(href='/' name='cancel' role='button') 取消
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//-
|
//-
|
||||||
ztncui - ZeroTier network controller UI
|
ztncui - ZeroTier network controller UI
|
||||||
Copyright (C) 2017-2021 Key Networks (https://key-networks.com)
|
Copyright (C) 2017-2023 Key Networks (https://key-networks.com)
|
||||||
Licensed under GPLv3 - see LICENSE for details.
|
Licensed under GPLv3 - see LICENSE for details.
|
||||||
|
|
||||||
extends network_layout
|
extends network_layout
|
||||||
@ -135,13 +135,15 @@ block net_content
|
|||||||
td
|
td
|
||||||
if (peer)
|
if (peer)
|
||||||
each path in peer.paths
|
each path in peer.paths
|
||||||
- const [ip, port] = path.address.split('/');
|
if (path.preferred == true)
|
||||||
= ip
|
- const [ip, port] = path.address.split('/');
|
||||||
span(style='color: gray;') /#{port}
|
= ip
|
||||||
= ' '
|
span(style='color: gray;') /#{port}
|
||||||
if (peer.latency != -1)
|
= ' '
|
||||||
br
|
if (peer.latency != -1)
|
||||||
| (#{peer.latency} ms)
|
br
|
||||||
|
| (#{peer.latency} ms)
|
||||||
|
- break
|
||||||
else
|
else
|
||||||
.alert.alert-info
|
.alert.alert-info
|
||||||
strong There are no members on this network - invite users to join #{network.nwid}
|
strong There are no members on this network - invite users to join #{network.nwid}
|
||||||
|
Loading…
Reference in New Issue
Block a user