Line data Source code
1 : /*
2 : http_config.c
3 : Created by Danny Goossen, Gioxa Ltd on 8/3/17.
4 :
5 : MIT License
6 :
7 : Copyright (c) 2017 deployctl, Gioxa Ltd.
8 :
9 : Permission is hereby granted, free of charge, to any person obtaining a copy
10 : of this software and associated documentation files (the "Software"), to deal
11 : in the Software without restriction, including without limitation the rights
12 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 : copies of the Software, and to permit persons to whom the Software is
14 : furnished to do so, subject to the following conditions:
15 :
16 : The above copyright notice and this permission notice shall be included in all
17 : copies or substantial portions of the Software.
18 :
19 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 : SOFTWARE.
26 :
27 : */
28 :
29 :
30 :
31 : #include "deployd.h"
32 :
33 : // TODO, read from /opt/config/nginx/....config.in
34 : // and regex the hostname
35 : // For now staic compiled here
36 :
37 : const char* def_http_conf=
38 :
39 : "server {\n" \
40 : " listen 80;\n listen [::]:80;\n" \
41 : " server_name %s;\n" \
42 : " server_tokens off;\n" \
43 : " client_max_body_size 0;\n" \
44 : " index index.html; \n" \
45 : " root %s/public;\n"\
46 : " error_page 404 /html_error/404.html; \n" \
47 : " location / {try_files $uri $uri/ =404;}\n" \
48 : " %s" \
49 : " location /html_error/ { root /opt/deploy/config; }\n" \
50 : " location ^~ /.well-known { root /opt/deploy/var; }\n" \
51 : " }\n";
52 :
53 : const char* def_https_conf=
54 : "server {\n" \
55 : " listen 80;\n listen [::]:80;\n" \
56 : " server_name %s;\n" \
57 : " server_tokens off;\n" \
58 : " client_max_body_size 0;\n" \
59 : " index index.html; \n" \
60 : " root %s/public;\n" \
61 : " error_page 404 /html_error/404.html; \n" \
62 : " location / { return 301 https://%s$request_uri; }\n" \
63 : " location /html_error/ { root /opt/deploy/config; }\n" \
64 : " location ^~ /.well-known { root /opt/deploy/var; }\n" \
65 : " }\n" \
66 : "server {\n" \
67 : " listen 443 ssl http2;\n listen [::]:443 ssl http2;\n" \
68 : " server_name %s;\n" \
69 : " server_tokens off;\n" \
70 : " client_max_body_size 0;\n" \
71 : " ssl on;\n" \
72 : " ssl_certificate /opt/deploy/.acme.sh/%s/fullchain.cer;\n" \
73 : " ssl_certificate_key /opt/deploy/.acme.sh/%s/%s.key;\n" \
74 : " ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4';\n" \
75 : " ssl_prefer_server_ciphers on;\n" \
76 : " ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n" \
77 : " ssl_session_cache builtin:1000 shared:SSL:10m;\n" \
78 : " ssl_session_timeout 5m;\n" \
79 : " index index.html;\n" \
80 : " root %s/public;\n"\
81 : " error_page 404 /html_error/404.html;\n"\
82 : " location / {try_files $uri $uri/ =404;}\n" \
83 : " %s" \
84 : " location /html_error/ {root /opt/deploy/config;}\n" \
85 : " }\n";
86 :
87 : const char * repo_conf_options=
88 : " location ~ ^/(rpm|deb)/([0-9a-zA-Z\\x20\\.]+)/([0-9a-z\\_]+) {autoindex on;}" \
89 : " location ~* ^/(deb|rpm)/ {return 301 $scheme://$server_name/;}";
90 :
91 : /*------------------------------------------------------------------------
92 : * url_check,
93 : * writes the commit tag in basePATH/public alias baseHREF
94 : * and performs a CURL to baseHref
95 : * to verify if baseHREF points to basePATH/public
96 : * Returns 0 on success
97 : *------------------------------------------------------------------------*/
98 3 : int url_check(void * opaque, char * basePATH,char * baseHREF)
99 : { // write http config for production environment
100 : char * newarg[11];
101 : // feedback buffer
102 3 : int exitcode=0;
103 3 : struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
104 : // env json
105 3 : cJSON * env_json=((data_exchange_t *)opaque)->env_json;
106 :
107 : // no need to set environment for individual commands
108 3 : ((data_exchange_t *)opaque)->needenvp=0;
109 :
110 3 : void * saved_args=((data_exchange_t *)opaque)->paramlist;
111 3 : ((data_exchange_t *)opaque)->paramlist=(char **)newarg;
112 :
113 : char filepath[1024];
114 3 : char * commit_sha=cJSON_GetObjectItem(env_json, "CI_COMMIT_SHA")->valuestring;
115 : // ***** write index.html of the tag
116 3 : newarg[0]="write_file";
117 3 : newarg[1]=filepath;
118 3 : sprintf(filepath, "%s/public/.commit_sha.txt",basePATH);
119 3 : newarg[2]=commit_sha;
120 3 : newarg[3]=NULL;
121 :
122 3 : Write_dyn_trace(trace, yellow," Check url:");
123 3 : Write_dyn_trace(trace, cyan," %s\n",baseHREF);
124 :
125 3 : Write_dyn_trace_pad(trace, none,75,"+ validate domain ...");
126 :
127 :
128 3 : debug("writing %s %d bytes\n",newarg[1],strlen(newarg[2]));
129 3 : exitcode=cmd_write(opaque);
130 3 : if (exitcode)
131 : {
132 1 : debug("failed writing hash to %s\n",newarg[1]);
133 1 : Write_dyn_trace(trace, red,"[FAILED]\n ERROR: Failed to write validation hash\n");
134 : }
135 3 : if (!exitcode)
136 : {
137 2 : usleep(500000);// wait till file change propagated
138 2 : exitcode=url_verify( commit_sha,baseHREF,trace);
139 : }
140 : //Restore arguments
141 3 : ((data_exchange_t *)opaque)->paramlist=saved_args;
142 3 : return exitcode;
143 : }
144 :
145 : /*------------------------------------------------------------------------
146 : * delete http config file in basePATH
147 : *------------------------------------------------------------------------*/
148 1 : int delete_http_config(void * opaque,char * basePATH)
149 : {
150 : char * newarg[4];
151 : char config_file_name[1024];
152 : // feedback buffer
153 1 : int exitcode=0;
154 1 : struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
155 : // env json
156 :
157 : // no need to set environment for individual commands
158 1 : ((data_exchange_t *)opaque)->needenvp=0;
159 :
160 1 : void * saved_args=((data_exchange_t *)opaque)->paramlist;
161 1 : ((data_exchange_t *)opaque)->paramlist=(char **)newarg;
162 1 : sprintf((char *)config_file_name,"%s/server.conf",basePATH);
163 1 : newarg[0]="/bin/rm";
164 1 : newarg[1]="-f";
165 1 : newarg[2]=(char *)config_file_name;
166 1 : newarg[3]=NULL;
167 :
168 1 : Write_dyn_trace(trace, none,"+ Remove new offending config-file\n");
169 :
170 1 : update_details(trace);
171 1 : debug("cmd: %s %s %s \n",newarg[0],newarg[1],newarg[2]);
172 :
173 1 : int temp=cmd_exec(opaque);
174 :
175 1 : if (temp) {debug("Failed to delete config %s\n",newarg[2]);}
176 1 : ((data_exchange_t *)opaque)->paramlist=saved_args;
177 1 : return exitcode;
178 : }
179 :
180 : /*------------------------------------------------------------------------
181 : * write_http_config for http or https with http to https redirect
182 : * write as root:nginx
183 : * returns 0 on succes
184 : *------------------------------------------------------------------------*/
185 3 : int write_http_config(void * opaque, int is_https,char * basePATH, char * server_name)
186 : { // write http config for production environment
187 : char * newarg[4];
188 : char config_file_name[1024];
189 : char config_file[4096];
190 : // feedback buffer
191 3 : int exitcode=0;
192 3 : struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
193 :
194 :
195 : // no need to set environment for individual commands
196 3 : ((data_exchange_t *)opaque)->needenvp=0;
197 :
198 3 : void * saved_args=((data_exchange_t *)opaque)->paramlist;
199 3 : ((data_exchange_t *)opaque)->paramlist=(char **)newarg;
200 :
201 3 : sprintf((char *)config_file_name,"%s/server.conf",basePATH);
202 3 : const char * conf_options=NULL;
203 3 : const char def_conf_options[]="#\n";
204 3 : conf_options=def_conf_options;
205 :
206 3 : if (is_https>1) conf_options=repo_conf_options;
207 :
208 3 : if (is_https==1||is_https==3)
209 : {
210 1 : sprintf((char *)config_file,def_https_conf,server_name,basePATH,server_name,server_name,server_name,server_name,server_name,basePATH,conf_options);
211 1 : newarg[0]="+ write_config_https...";
212 :
213 : }
214 : else //if (is_https==0|| is_https==2 )
215 : {
216 2 : sprintf((char *)config_file,def_http_conf,server_name,basePATH,conf_options);
217 2 : newarg[0]="+ write_config_http...";
218 :
219 : }
220 :
221 :
222 : // ***** write config
223 3 : newarg[1]=(char *)config_file_name;
224 3 : newarg[2]=(char *)config_file;
225 3 : newarg[3]=NULL;
226 3 : Write_dyn_trace_pad(trace, none,75,"%s",newarg[0]);
227 3 : debug("writing %s %d bytes\n",newarg[1],strlen(newarg[2]));
228 3 : exitcode=cmd_write(opaque);
229 :
230 3 : if (!exitcode)
231 : {
232 2 : debug("wrote %s, %d bytes\n",newarg[1],strlen(newarg[2]));
233 2 : Write_dyn_trace(trace, green,"[OK]\n");
234 : }
235 : else
236 : {
237 1 : debug("ERROR: Writing %s, %d bytes\n",newarg[1],strlen(newarg[2]));
238 1 : Write_dyn_trace(trace, red,"[FAILED]\n ERROR: writing new config-file\n");
239 : }
240 3 : update_details(trace);
241 3 : ((data_exchange_t *)opaque)->paramlist=saved_args;
242 3 : return exitcode;
243 : }
244 :
245 : /*------------------------------------------------------------------------
246 : * check_namespace
247 : * read the basePATH/.namespace file
248 : * should contain the project_url from previous deployment
249 : * If that matches the current project url, it returns 0
250 : * if no content or no file it returns also 0
251 : *------------------------------------------------------------------------*/
252 5 : int check_namespace(void * opaque,char * filepath)
253 : {
254 5 : int result=0;
255 : char namespace_file_name[1024];
256 5 : int exitcode=0;
257 5 : struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
258 :
259 5 : cJSON * env_json=((data_exchange_t *)opaque)->env_json;
260 5 : char * project_url=cJSON_get_key(env_json, "CI_PROJECT_URL");
261 5 : sprintf((char *)namespace_file_name,"%s/.namespace",filepath);
262 :
263 5 : Write_dyn_trace_pad(trace, none,75,"+ Check name space ...");
264 :
265 5 : FILE *f = fopen(namespace_file_name, "r");
266 5 : if (f == NULL)
267 : {// doesn't exist, so ok
268 3 : Write_dyn_trace(trace, green,"[OK]\n");
269 :
270 3 : return 0;
271 : }
272 : struct MemoryStruct mem;
273 2 : init_dynamicbuf(&mem);
274 : char buf[1024];
275 2 : bzero(buf, 1024);
276 6 : while( fgets( buf, 1024, f ) != NULL )
277 : {
278 2 : Writedynamicbuf(buf, &mem );
279 2 : bzero(buf, 1024);
280 : }
281 2 : fclose(f);
282 2 : result=strcmp(mem.memory,project_url);
283 :
284 2 : if (result)
285 : {
286 1 : debug("ERR: namespace : \n => %s\n != \n =>%s\n",project_url,mem.memory);
287 1 : Write_dyn_trace(trace, red,"[FAILED]\n ERROR: project_url : \n =>%s\n != \n =>%s\n",project_url,mem.memory);
288 :
289 1 : exitcode=1;
290 : }
291 : else
292 : {
293 1 : Write_dyn_trace(trace, green,"[OK]\n");
294 1 : exitcode=0;
295 : }
296 2 : update_details(trace);
297 : // TODO
298 : // to avoid deliberate spoofing of namespaces and does to sabotage somebody else release:
299 : // check if CI_REPOSITORY_URL points to the same repo as CI_PROJECT_URL
300 : // then git ls-remote CI_REPOSITORY_URL if ok, at least we got access, better to access with API, but doesn't work if not public repo.
301 :
302 : // for now, unintentional overwrite protection is provided, room for discussion and brainstorm
303 2 : return exitcode;
304 : }
305 :
306 : /*------------------------------------------------------------------------
307 : * write_namespace, or locks current directory on this project
308 : * writes the project_url in basePATH/.namespace
309 : * return 0 on success
310 : *------------------------------------------------------------------------*/
311 1 : int write_namespace(void * opaque, char * filepath )
312 : {
313 : char * newarg[4];
314 : char namespace_file_name[1024];
315 : // feedback buffer
316 1 : int exitcode=0;
317 1 : struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
318 :
319 : // env json
320 1 : cJSON * env_json=((data_exchange_t *)opaque)->env_json;
321 :
322 : // no need to set environment for individual commands
323 1 : ((data_exchange_t *)opaque)->needenvp=0;
324 :
325 1 : void * saved_args=((data_exchange_t *)opaque)->paramlist;
326 1 : ((data_exchange_t *)opaque)->paramlist=(char **)newarg;
327 :
328 1 : char * project_url=cJSON_get_key(env_json, "CI_PROJECT_URL");
329 :
330 1 : newarg[0]="write_name_space";
331 1 : newarg[1]=(char *)namespace_file_name;
332 1 : newarg[2]=(char *)project_url;
333 1 : newarg[3]=NULL;
334 1 : Write_dyn_trace_pad(trace, none,75,"+ Lock name space ...");
335 :
336 1 : sprintf((char *)namespace_file_name,"%s/.namespace",filepath);
337 1 : exitcode=cmd_write(opaque);
338 1 : if (!exitcode)
339 : {
340 1 : debug("wrote namespace %s: %s\n",newarg[1],newarg[2]);
341 : }
342 1 : if (!exitcode)
343 1 : Write_dyn_trace(trace, green,"[OK]\n");
344 : else
345 0 : Write_dyn_trace(trace, red,"[FAILED]\n");
346 :
347 1 : ((data_exchange_t *)opaque)->paramlist=saved_args;
348 :
349 1 : update_details(trace);
350 1 : return exitcode;
351 : }
|