1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
'use strict';
'require form';
'require fs';
'require uci';
'require ui';
'require view';
return view.extend({
load() {
return L.resolveDefault(fs.list('/root/.ssh/'), []).then(function (entries) {
const sshKeyNames = _findAllPossibleIdKeys(entries);
return Promise.resolve(sshKeyNames);
});
},
render([sshKeyNames]) {
if (sshKeyNames.length === 0) {
ui.addNotification(null, E('p', _('No SSH keys found, <a %s>generate a new one</a>').format('href="./ssh_keys"')), 'warning');
}
let m, s, o;
m = new form.Map('sshtunnel', _('SSH Tunnels'),
_('This configures <a %s>SSH Tunnels</a>.')
.format('href="https://openwrt.org/docs/guide-user/services/ssh/sshtunnel"')
);
s = m.section(form.GridSection, 'server', _('Servers'));
s.anonymous = false;
s.addremove = true;
s.nodescriptions = true;
o = s.tab('general', _('General Settings'));
o = s.tab('advanced', _('Advanced Settings'));
o = s.taboption('general', form.Value, 'hostname', _('Hostname'));
o.placeholder = 'example.com';
o.datatype = 'host';
o.rmempty = false;
o = s.taboption('general', form.Value, 'port', _('Port'));
o.placeholder = '22';
o.datatype = 'port';
o = s.taboption('general', form.Value, 'user', _('User'));
o.default = 'root';
o = s.taboption('general', form.ListValue, 'IdentityFile', _('Identity Key'),
_('Private key file with authentication identity.') + '<br />' +
_('If not specified then a default will be used.') + '<br />' +
_('For Dropbear %s').format('<code>id_dropbear</code>') + '<br />' +
_('For OpenSSH %s').format('<code>id_rsa, id_ed25519, id_ecdsa</code>') +
_manSshConfig('IdentityFile')
);
o.value('');
for (let sshKeyName of sshKeyNames) {
o.value('/root/.ssh/' + sshKeyName, sshKeyName);
}
o.optional = true;
o = s.taboption('advanced', form.ListValue, 'LogLevel', _('Log level'));
o.value('QUIET', 'QUIET');
o.value('FATAL', 'FATAL');
o.value('ERROR', 'ERROR');
o.value('INFO', 'INFO');
o.value('VERBOSE', 'VERBOSE');
o.value('DEBUG', 'DEBUG');
o.value('DEBUG2', 'DEBUG2');
o.value('DEBUG3', 'DEBUG3');
o.default = 'INFO';
o.modalonly = true;
o = s.taboption('advanced', form.ListValue, 'Compression', _('Use compression'),
_('Compression may be useful on slow connections.') +
_manSshConfig('Compression')
);
o.value('yes', _('Yes'));
o.value('no', _('No'));
o.default = 'no';
o.modalonly = true;
o = s.taboption('advanced', form.Value, 'retrydelay', _('Retry delay'),
_('Delay after a connection failure before trying to reconnect.')
);
o.placeholder = '10';
o.default = '10';
o.datatype = 'uinteger';
o.optional = true;
o.modalonly = true;
o = s.taboption('advanced', form.Value, 'ServerAliveCountMax', _('Server keep alive attempts'),
_('The number of server alive messages which may be sent before SSH disconnects from the server.') +
_manSshConfig('ServerAliveCountMax')
);
o.placeholder = '3';
o.datatype = 'uinteger';
o.optional = true;
o.modalonly = true;
o = s.taboption('advanced', form.Value, 'ServerAliveInterval', _('Server keep alive interval (seconds)'),
_manSshConfig('ServerAliveInterval')
);
o.optional = true;
o.default = '60';
o.datatype = 'uinteger';
o.modalonly = true;
o = s.taboption('advanced', form.ListValue, 'CheckHostIP', _('Check host IP'),
_('Check the host IP address in the %s file.').format('<code>known_hosts</code>') + '<br />' +
_('This allows SSH to detect whether a host key changed due to DNS spoofing.') +
_manSshConfig('CheckHostIP')
);
o.value('yes', _('Yes'));
o.value('no', _('No'));
o.default = 'no';
o.modalonly = true;
o = s.taboption('advanced', form.ListValue, 'StrictHostKeyChecking', _('Strict host key checking'),
_('Refuse to connect to hosts whose host key has changed.') +
_manSshConfig('StrictHostKeyChecking')
);
o.value('accept-new', _('Accept new and check if not changed'));
o.value('yes', _('Yes'));
o.value('no', _('No'));
o.default = 'accept-new';
o.modalonly = true;
o = s.taboption('advanced', form.Value, 'ProxyCommand', _('Proxy tunnel command'),
_('The command to use to connect to the server.') + '<br />' +
_('For example, the following command would connect via an HTTP proxy:') + '<br />' +
'<code>ncat --proxy-type http --proxy-auth alice:secret --proxy 192.168.1.2:8080 %h %p</code>' +
_manSshConfig('ProxyCommand')
);
o.modalonly = true;
return m.render();
},
});
function _findAllPossibleIdKeys(entries) {
const sshKeyNames = new Set();
const fileNames = entries.filter(item => item.type === 'file').map(item => item.name);
for (let fileName of fileNames) {
// a key file should have a corresponding .pub file
if (fileName.endsWith('.pub')) {
let sshKeyName = fileName.slice(0, -4);
// if such a key exists then add it
if (fileNames.includes(sshKeyName)) {
sshKeyNames.add(sshKeyName);
}
} else {
// or at least it should start with id_ e.g. id_dropbear
if (fileName.startsWith('id_')) {
let sshKeyName = fileName;
sshKeyNames.add(sshKeyName);
}
}
}
return Array.from(sshKeyNames);
}
function _manSshConfig(opt) {
return '<br />' + _('See %s.')
.format('<a target="_blank" href="https://manpages.debian.org/testing/openssh-client/ssh_config.5#'+ opt + '">ssh_config ' + opt + '</a>');
}
|