]> git.proxmox.com Git - pve-manager.git/blame - PVE/HTTPServer.pm
takeover CertCache from pve-cluster
[pve-manager.git] / PVE / HTTPServer.pm
CommitLineData
57f93db1
DM
1package PVE::HTTPServer;
2
3use strict;
4use warnings;
d06a1c62 5
b996e6c0
DM
6use PVE::SafeSyslog;
7use PVE::INotify;
8use PVE::Tools;
9use PVE::APIServer::AnyEvent;
46095dd4 10use PVE::Exception qw(raise_param_exc raise);
c2e9823c 11
b996e6c0
DM
12use PVE::RPCEnvironment;
13use PVE::AccessControl;
0f9ac2df 14use PVE::CertCache;
b996e6c0 15use PVE::Cluster;
3c54bc91 16use PVE::API2Tools;
c2e9823c 17
b996e6c0 18use Data::Dumper;
57f93db1 19
b996e6c0 20use base('PVE::APIServer::AnyEvent');
353fef24 21
b996e6c0 22use HTTP::Status qw(:constants);
353fef24 23
57f93db1
DM
24sub new {
25 my ($this, %args) = @_;
26
27 my $class = ref($this) || $this;
28
b996e6c0
DM
29 my $self = $class->SUPER::new(%args);
30
f91072d5 31 $self->{rpcenv} = PVE::RPCEnvironment->init(
353fef24 32 $self->{trusted_env} ? 'priv' : 'pub', atfork => sub { $self-> atfork_handler() });
f91072d5 33
b996e6c0
DM
34 return $self;
35}
02667982 36
b996e6c0
DM
37sub verify_spice_connect_url {
38 my ($self, $connect_str) = @_;
57f93db1 39
9d3f059f
DM
40 my $rpcenv = $self->{rpcenv};
41
42 $rpcenv->init_request();
43
b996e6c0 44 my ($vmid, $node, $port) = PVE::AccessControl::verify_spice_connect_url($connect_str);
57f93db1 45
b996e6c0
DM
46 return ($vmid, $node, $port);
47}
57f93db1 48
b996e6c0
DM
49sub generate_csrf_prevention_token {
50 my ($username) = @_;
f91072d5 51
b996e6c0 52 return PVE::AccessControl::assemble_csrf_prevention_token($username);
57f93db1
DM
53}
54
430b554f 55sub auth_handler {
9d3f059f 56 my ($self, $method, $rel_uri, $ticket, $token, $peer_host) = @_;
430b554f
DM
57
58 my $rpcenv = $self->{rpcenv};
59
9d3f059f
DM
60 # set environment variables
61 $rpcenv->set_user(undef);
62 $rpcenv->set_language('C');
63 $rpcenv->set_client_ip($peer_host);
64
46095dd4
TL
65 eval { $rpcenv->init_request() };
66 raise("RPCEnvironment init request failed: $@\n") if $@;
a2e669fe 67
430b554f
DM
68 my $require_auth = 1;
69
70 # explicitly allow some calls without auth
71 if (($rel_uri eq '/access/domains' && $method eq 'GET') ||
72 ($rel_uri eq '/access/ticket' && ($method eq 'GET' || $method eq 'POST'))) {
73 $require_auth = 0;
74 }
75
76 my ($username, $age);
77
78 my $isUpload = 0;
79
80 if ($require_auth) {
81
82 die "No ticket\n" if !$ticket;
83
af81354f 84 ($username, $age, my $tfa_info) = PVE::AccessControl::verify_ticket($ticket);
7541d6a7 85
af81354f
WB
86 if (defined($tfa_info)) {
87 if (defined(my $challenge = $tfa_info->{challenge})) {
88 $rpcenv->set_u2f_challenge($challenge);
89 }
7541d6a7 90 die "No ticket\n"
24d2ed8c 91 if ($rel_uri ne '/access/tfa' || $method ne 'POST');
7541d6a7 92 }
430b554f
DM
93
94 $rpcenv->set_user($username);
95
96 if ($method eq 'POST' && $rel_uri =~ m|^/nodes/([^/]+)/storage/([^/]+)/upload$|) {
97 my ($node, $storeid) = ($1, $2);
98 # we disable CSRF checks if $isUpload is set,
99 # to improve security we check user upload permission here
100 my $perm = { check => ['perm', "/storage/$storeid", ['Datastore.AllocateTemplate']] };
101 $rpcenv->check_api2_permissions($perm, $username, {});
102 $isUpload = 1;
103 }
104
105 # we skip CSRF check for file upload, because it is
106 # difficult to pass CSRF HTTP headers with native html forms,
107 # and it should not be necessary at all.
108 my $euid = $>;
109 PVE::AccessControl::verify_csrf_prevention_token($username, $token)
110 if !$isUpload && ($euid != 0) && ($method ne 'GET');
111 }
112
113 return {
114 ticket => $ticket,
115 token => $token,
116 userid => $username,
117 age => $age,
118 isUpload => $isUpload,
119 };
120}
121
2261f249
DM
122sub rest_handler {
123 my ($self, $clientip, $method, $rel_uri, $auth, $params) = @_;
124
125 my $rpcenv = $self->{rpcenv};
126
70473e51
DM
127 my $resp = {
128 status => HTTP_NOT_IMPLEMENTED,
129 message => "Method '$method $rel_uri' not implemented",
130 };
131
132 my ($handler, $info);
133
134 eval {
135 my $uri_param = {};
136 ($handler, $info) = PVE::API2->find_handler($method, $rel_uri, $uri_param);
137 return if !$handler || !$info;
2261f249 138
70473e51
DM
139 foreach my $p (keys %{$params}) {
140 if (defined($uri_param->{$p})) {
141 raise_param_exc({$p => "duplicate parameter (already defined in URI)"});
142 }
143 $uri_param->{$p} = $params->{$p};
2261f249 144 }
2261f249 145
70473e51
DM
146 # check access permissions
147 $rpcenv->check_api2_permissions($info->{permissions}, $auth->{userid}, $uri_param);
2261f249 148
3c54bc91
DM
149 if ($info->{proxyto} || $info->{proxyto_callback}) {
150 my $node = PVE::API2Tools::resolve_proxyto(
151 $rpcenv, $info->{proxyto_callback}, $info->{proxyto}, $uri_param);
2261f249
DM
152
153 if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
154 die "unable to proxy file uploads" if $auth->{isUpload};
70473e51
DM
155 my $remip = $self->remote_node_ip($node);
156 $resp = { proxy => $remip, proxynode => $node, proxy_params => $params };
157 return;
2261f249 158 }
2261f249 159 }
2261f249 160
70473e51
DM
161 my $euid = $>;
162 if ($info->{protected} && ($euid != 0)) {
163 $resp = { proxy => 'localhost' , proxy_params => $params };
164 return;
165 }
2261f249 166
70473e51
DM
167 $resp = {
168 data => $handler->handle($info, $uri_param),
169 info => $info, # useful to format output
170 status => HTTP_OK,
171 };
2261f249
DM
172
173 if (my $count = $rpcenv->get_result_attrib('total')) {
174 $resp->{total} = $count;
175 }
70473e51 176
2261f249
DM
177 if (my $diff = $rpcenv->get_result_attrib('changes')) {
178 $resp->{changes} = $diff;
179 }
180 };
70473e51 181 my $err = $@;
2261f249 182
9d3f059f 183 $rpcenv->set_user(undef); # clear after request
70473e51
DM
184
185 if ($err) {
186 $resp = { info => $info };
187 if (ref($err) eq "PVE::Exception") {
188 $resp->{status} = $err->{code} || HTTP_INTERNAL_SERVER_ERROR;
189 $resp->{errors} = $err->{errors} if $err->{errors};
190 $resp->{message} = $err->{msg};
191 } else {
192 $resp->{status} = HTTP_INTERNAL_SERVER_ERROR;
193 $resp->{message} = $err;
194 }
195 }
196
2261f249
DM
197 return $resp;
198}
430b554f 199
7e5f7c61
DM
200sub check_cert_fingerprint {
201 my ($self, $cert) = @_;
202
0f9ac2df 203 return PVE::CertCache::check_cert_fingerprint($cert);
7e5f7c61
DM
204}
205
206sub initialize_cert_cache {
207 my ($self, $node) = @_;
208
0f9ac2df 209 PVE::CertCache::initialize_cert_cache($node);
7e5f7c61
DM
210}
211
212sub remote_node_ip {
213 my ($self, $node) = @_;
214
215 my $remip = PVE::Cluster::remote_node_ip($node);
216
217 die "unable to get remote IP address for node '$node'\n" if !$remip;
218
219 return $remip;
220}
221
57f93db1 2221;