]>
Commit | Line | Data |
---|---|---|
4a17e72e DM |
1 | package PVE::Service::pveproxy; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::SafeSyslog; | |
7 | use PVE::Daemon; | |
8 | use HTTP::Response; | |
9 | use Encode; | |
10 | use URI; | |
11 | use URI::QueryParam; | |
4a17e72e DM |
12 | use Data::Dumper; |
13 | use PVE::API2Tools; | |
14 | use PVE::API2; | |
b996e6c0 DM |
15 | use PVE::APIServer::Formatter; |
16 | use PVE::APIServer::Formatter::Standard; | |
17 | use PVE::APIServer::Formatter::HTML; | |
18 | use PVE::APIServer::AnyEvent; | |
4a17e72e DM |
19 | use PVE::HTTPServer; |
20 | ||
184825e1 | 21 | use Template; |
4a17e72e DM |
22 | |
23 | use PVE::Tools; | |
24 | ||
25 | use base qw(PVE::Daemon); | |
26 | ||
27 | my $cmdline = [$0, @ARGV]; | |
28 | ||
29 | my %daemon_options = ( | |
30 | max_workers => 3, | |
31 | restart_on_error => 5, | |
32 | stop_wait_time => 15, | |
33 | leave_children_open_on_reload => 1, | |
34 | setuid => 'www-data', | |
35 | setgid => 'www-data', | |
36 | pidfile => '/var/run/pveproxy/pveproxy.pid', | |
37 | ); | |
38 | ||
39 | my $daemon = __PACKAGE__->new('pveproxy', $cmdline, %daemon_options); | |
40 | ||
41 | sub add_dirs { | |
42 | my ($result_hash, $alias, $subdir) = @_; | |
43 | ||
e4697709 | 44 | PVE::APIServer::AnyEvent::add_dirs($result_hash, $alias, $subdir); |
4a17e72e DM |
45 | } |
46 | ||
245e567e DC |
47 | my $basedirs = { |
48 | novnc => '/usr/share/novnc-pve', | |
49 | extjs => '/usr/share/javascript/extjs', | |
50 | manager => '/usr/share/pve-manager', | |
51 | docs => '/usr/share/pve-docs', | |
245e567e DC |
52 | }; |
53 | ||
4a17e72e DM |
54 | sub init { |
55 | my ($self) = @_; | |
56 | ||
57 | # we use same ALLOW/DENY/POLICY as pveproxy | |
58 | my $proxyconf = PVE::API2Tools::read_proxy_config(); | |
59 | ||
60 | my $accept_lock_fn = "/var/lock/pveproxy.lck"; | |
61 | ||
62 | my $lockfh = IO::File->new(">>${accept_lock_fn}") || | |
63 | die "unable to open lock file '${accept_lock_fn}' - $!\n"; | |
64 | ||
65 | my $family = PVE::Tools::get_host_address_family($self->{nodename}); | |
66 | my $socket = $self->create_reusable_socket(8006, undef, $family); | |
67 | ||
68 | my $dirs = {}; | |
69 | ||
245e567e DC |
70 | add_dirs($dirs, '/pve2/locale/', "$basedirs->{manager}/locale/"); |
71 | add_dirs($dirs, '/pve2/touch/', "$basedirs->{manager}/touch/"); | |
72 | add_dirs($dirs, '/pve2/ext6/', "$basedirs->{extjs}/"); | |
73 | add_dirs($dirs, '/pve2/images/' => "$basedirs->{manager}/images/"); | |
74 | add_dirs($dirs, '/pve2/css/' => "$basedirs->{manager}/css/"); | |
75 | add_dirs($dirs, '/pve2/js/' => "$basedirs->{manager}/js/"); | |
76 | add_dirs($dirs, '/pve-docs/' => "$basedirs->{docs}/"); | |
245e567e | 77 | add_dirs($dirs, '/novnc/' => "$basedirs->{novnc}/"); |
4a17e72e DM |
78 | |
79 | $self->{server_config} = { | |
a9de2d44 | 80 | title => 'Proxmox VE API', |
4a17e72e DM |
81 | keep_alive => 100, |
82 | max_conn => 500, | |
83 | max_requests => 1000, | |
84 | lockfile => $accept_lock_fn, | |
85 | socket => $socket, | |
86 | lockfh => $lockfh, | |
87 | debug => $self->{debug}, | |
88 | trusted_env => 0, # not trusted, anyone can connect | |
89 | logfile => '/var/log/pveproxy/access.log', | |
90 | allow_from => $proxyconf->{ALLOW_FROM}, | |
91 | deny_from => $proxyconf->{DENY_FROM}, | |
92 | policy => $proxyconf->{POLICY}, | |
93 | ssl => { | |
94 | # Note: older versions are considered insecure, for example | |
95 | # search for "Poodle"-Attac | |
ee0b96b1 | 96 | method => 'any', |
4a17e72e DM |
97 | sslv2 => 0, |
98 | sslv3 => 0, | |
99 | cipher_list => $proxyconf->{CIPHERS} || 'HIGH:MEDIUM:!aNULL:!MD5', | |
100 | key_file => '/etc/pve/local/pve-ssl.key', | |
101 | cert_file => '/etc/pve/local/pve-ssl.pem', | |
102 | }, | |
103 | # Note: there is no authentication for those pages and dirs! | |
104 | pages => { | |
c7f32808 | 105 | '/' => sub { get_index($self->{nodename}, @_) }, |
4a17e72e DM |
106 | # avoid authentication when accessing favicon |
107 | '/favicon.ico' => { | |
245e567e | 108 | file => "$basedirs->{manager}/images/favicon.ico", |
4a17e72e DM |
109 | }, |
110 | }, | |
111 | dirs => $dirs, | |
112 | }; | |
41196653 FG |
113 | |
114 | if ($proxyconf->{DHPARAMS}) { | |
115 | $self->{server_config}->{ssl}->{dh_file} = $proxyconf->{DHPARAMS}; | |
116 | } else { | |
117 | $self->{server_config}->{ssl}->{dh} = 'skip2048'; | |
118 | } | |
299d290c FG |
119 | |
120 | if (-f '/etc/pve/local/pveproxy-ssl.pem' && -f '/etc/pve/local/pveproxy-ssl.key') { | |
121 | $self->{server_config}->{ssl}->{cert_file} = '/etc/pve/local/pveproxy-ssl.pem'; | |
122 | $self->{server_config}->{ssl}->{key_file} = '/etc/pve/local/pveproxy-ssl.key'; | |
123 | syslog('info', 'Using \'/etc/pve/local/pveproxy-ssl.pem\' as certificate for the web interface.'); | |
124 | } | |
4a17e72e DM |
125 | } |
126 | ||
127 | sub run { | |
128 | my ($self) = @_; | |
129 | ||
130 | my $server = PVE::HTTPServer->new(%{$self->{server_config}}); | |
131 | $server->run(); | |
132 | } | |
133 | ||
134 | $daemon->register_start_command(); | |
135 | $daemon->register_restart_command(1); | |
136 | $daemon->register_stop_command(); | |
137 | $daemon->register_status_command(); | |
138 | ||
139 | our $cmddef = { | |
140 | start => [ __PACKAGE__, 'start', []], | |
141 | restart => [ __PACKAGE__, 'restart', []], | |
142 | stop => [ __PACKAGE__, 'stop', []], | |
143 | status => [ __PACKAGE__, 'status', [], undef, sub { print shift . "\n";} ], | |
144 | }; | |
145 | ||
146 | sub is_phone { | |
147 | my ($ua) = @_; | |
148 | ||
149 | return 0 if !$ua; | |
150 | ||
151 | return 1 if $ua =~ m/(iPhone|iPod|Windows Phone)/; | |
152 | ||
153 | if ($ua =~ m/Mobile(\/|\s)/) { | |
154 | return 1 if $ua =~ m/(BlackBerry|BB)/; | |
155 | return 1 if ($ua =~ m/(Android)/) && ($ua !~ m/(Silk)/); | |
156 | } | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | # NOTE: Requests to those pages are not authenticated | |
162 | # so we must be very careful here | |
163 | ||
164 | sub get_index { | |
c7f32808 | 165 | my ($nodename, $server, $r, $args) = @_; |
4a17e72e DM |
166 | |
167 | my $lang = 'en'; | |
168 | my $username; | |
169 | my $token = 'null'; | |
170 | ||
171 | if (my $cookie = $r->header('Cookie')) { | |
172 | if (my $newlang = ($cookie =~ /(?:^|\s)PVELangCookie=([^;]*)/)[0]) { | |
173 | if ($newlang =~ m/^[a-z]{2,3}(_[A-Z]{2,3})?$/) { | |
174 | $lang = $newlang; | |
175 | } | |
176 | } | |
b996e6c0 | 177 | my $ticket = PVE::APIServer::Formatter::extract_auth_cookie($cookie, $server->{cookie_name}); |
4a17e72e DM |
178 | if (($username = PVE::AccessControl::verify_ticket($ticket, 1))) { |
179 | $token = PVE::AccessControl::assemble_csrf_prevention_token($username); | |
180 | } | |
181 | } | |
182 | ||
183 | $username = '' if !$username; | |
184 | ||
185 | my $mobile = is_phone($r->header('User-Agent')) ? 1 : 0; | |
186 | ||
187 | if (defined($args->{mobile})) { | |
188 | $mobile = $args->{mobile} ? 1 : 0; | |
189 | } | |
190 | ||
184825e1 DC |
191 | my $page = ''; |
192 | my $template = Template->new({ABSOLUTE => 1}); | |
193 | ||
194 | my $langfile = 0; | |
195 | ||
196 | if (-f "$basedirs->{manager}/locale/pve-lang-$lang.js") { | |
197 | $langfile = 1; | |
198 | } | |
199 | ||
200 | my $vars = { | |
201 | lang => $lang, | |
202 | langfile => $langfile, | |
203 | username => $username, | |
204 | token => $token, | |
205 | console => $args->{console}, | |
206 | nodename => $nodename, | |
207 | debug => $server->{debug}, | |
208 | }; | |
209 | ||
210 | # by default, load the normal index | |
211 | my $dir = $basedirs->{manager}; | |
4a17e72e DM |
212 | |
213 | if (defined($args->{console}) && $args->{novnc}) { | |
184825e1 | 214 | $dir = $basedirs->{novnc}; |
4a17e72e | 215 | } elsif ($mobile) { |
184825e1 | 216 | $dir = "$basedirs->{manager}/touch"; |
2ebf4aec | 217 | } |
184825e1 DC |
218 | |
219 | $template->process("$dir/index.html.tpl", $vars, \$page) | |
220 | || die $template->error(), "\n"; | |
4a17e72e DM |
221 | my $headers = HTTP::Headers->new(Content_Type => "text/html; charset=utf-8"); |
222 | my $resp = HTTP::Response->new(200, "OK", $headers, $page); | |
223 | ||
224 | return $resp; | |
225 | } | |
226 | ||
227 | 1; |