[{"content":"Intro Recently while browsing the internet I noticed a CTF challenge that was form of preselection to job interview. Generally I was not very interested with job in security field, but I wanted to try if I can solve any of the tasks without any preparation and generally I like challenges. I did some CTF\u0026rsquo;s in the past but it was a couple years ago when I was at the university and I think I wasn\u0026rsquo;t good at it. I solved only a couple from web app category, but other categories were usually to hard for me.\nChallenge The challenge consists of 7 (or 8) tasks. I am not sure if 8th task is valid or not because it was commented out on the site with CTF and it the challenge description was mentioned that you need to send them 7 flags.\n1 - Xcross-this There was not much on the page. Only the input filed and some numbers. I noticed that I can right click on the numbers and it showed that page is using MathJax library. I started to look for known XSS vulnerabilities is this library and found that there is one with \\unicode{} payload. I was trying different payloads, but I wasn\u0026rsquo;t sure what I want to achieve. Then I went to the browser console and noticed following error Uncaught ReferenceError: xss is not defined, so I went to the source page and noticed some script:\nfunction $_GET(name) { const url = new URL(location.href); return url.searchParams.get(name) || \u0026#39;\u0026#39;; } const query = $_GET(\u0026#39;xss\u0026#39;) || \u0026#39;\\\\frac 1 2\u0026#39;; // MathJax automatically renders text from within \u0026#39;$$\u0026#39; delimiters latex.textContent = \u0026#39;$$\u0026#39; + query + \u0026#39;$$\u0026#39;; xss.value = query; // Obsługuje kliknięcie w link const flagLink = document.getElementById(\u0026#39;flagLink\u0026#39;); flagLink.addEventListener(\u0026#39;click\u0026#39;, function(e) { e.preventDefault(); fetch(this.getAttribute(\u0026#39;href\u0026#39;)) .then(response =\u0026gt; response.json()) .then(data =\u0026gt; { if (data.flag) { alert(data.flag); } else { alert(\u0026#39;Nie udało się pobrać flagi.\u0026#39;); } }) .catch(error =\u0026gt; { console.error(error); alert(\u0026#39;Wystąpił błąd.\u0026#39;); }); }); So I set breakpoint to be able to define xss object before the exception. The script went on, but it was not able to find element with id flagLink. I thought that I can create one, but the problem is next the script tries to fetch URL form that element. So what URL should I set? The only clue was \u0026lt;a href=\u0026quot;?LogOut=1\u0026quot; id=\u0026quot;LogOut\u0026quot;\u0026gt;\u0026lt;/a\u0026gt; in page source code, so I changed id of that element, but I got the message that getting the flag failed. I looked at the response {\u0026quot;error\u0026quot;:\u0026quot;VVRGU1IxZ3pkRlJrU0VsM1ltMWtTbUpyTVdoa1IyZzJWMWM1TVZGWVNteG1VVzg5Q2c9PQo=\\n\u0026quot;} and I thought that is wrong way, so I started to look for different solution. After some time I went back to that idea and tried decode the response with base64, but it did not help. Without any hope I tried decode value again and again and it was it. The flag was encoded three times with base64.\n2 - Cross-site This task was commented out. I\u0026rsquo;ve been trying to solve this one but unfortunately I failed. The purpose of this task was to get flag from file get_flag.php using a search engine that was vulnerable to XSS. On the page code I found the script that was fetching content of get_flag.php and inserting the value in hidden input filed. I was not able to figure out how to modify the request to get the flag. I also did not want to spend too much time on task while I was not sure if this task is valid or not. This is the script from the task.\ndocument.addEventListener(\u0026#39;DOMContentLoaded\u0026#39;, () =\u0026gt; { // Wykonaj zapytanie AJAX do skryptu PHP fetch(\u0026#39;get_flag.php\u0026#39;) .then(response =\u0026gt; response.text()) .then(flagValue =\u0026gt; { // Tworzenie ukrytego elementu input i wstrzyknięcie flagi var secretCodeInput = document.createElement(\u0026#39;input\u0026#39;); secretCodeInput.type = \u0026#39;hidden\u0026#39;; secretCodeInput.className = \u0026#39;secretcode\u0026#39;; secretCodeInput.value = flagValue; // Dodanie elementu do ciała dokumentu document.body.appendChild(secretCodeInput); }); }); 3 - Database The task was to use SQL injection to get hidden job offer. I was just trying different SQL injection payloads and keep my fingers crossed. I noticed that I have to use ' at the end of the payload or I get SQL syntax error. The syntax error was from SQLite, so I stared to look for vulnerabilities for that database, but I did not found anything useful. I also tried use comment sign -- to affect the query, but it always gave me syntax error. After a while I found this payload in the internet at it worked; ?query=')+or+true--. The hidden job offer appeared with flag.\n4 - External Entity I think this task was the easiest. At the top of the page there was following code snipped $flag = @file_get_contents(\u0026quot;/tmp/flag\u0026quot;); and below that there was text area with following XML\n\u0026lt;creds\u0026gt; \u0026lt;user\u0026gt;admin\u0026lt;/user\u0026gt; \u0026lt;pass\u0026gt;admin\u0026lt;/pass\u0026gt; \u0026lt;/creds\u0026gt; When you sent that XML, you got a message that you are successfully logged in and your username. At that point I was not aware of any XML vulnerabilities, but I just google some XML exploits and found XML external entity injection which can be used to retrieve file. Thus it looked perfect. I just create payload with provided file path and username as defined entity.\n\u0026lt;!DOCTYPE foo [ \u0026lt;!ENTITY xxe SYSTEM \u0026#34;file:///tmp/flag\u0026#34;\u0026gt; ]\u0026gt; \u0026lt;creds\u0026gt; \u0026lt;user\u0026gt;\u0026amp;xxe;\u0026lt;/user\u0026gt; \u0026lt;pass\u0026gt;admin\u0026lt;/pass\u0026gt; \u0026lt;/creds\u0026gt; And now instead of username we got the content of /tmp/flag\n5 - deardir The task was about to provide path to file as a parameter file. The page source code also contained the comment with path /tmp/flag. It looked very simple path traversal vulnerability. When I tried to provide following parameter ?file=/tmp/flag server returned error message include(/var/www/html//tmp/flag): failed to open stream: No such file or directory, so it means that we are in /var/www/html/ dir. Thus we have to move three dirs up and then we can get to the /tmp/flag/ using following query ?file=../../../tmp/flag.\n6 - I\u0026rsquo;m brOken' In this task was a login page with testing account. After the logging in, a session cookie was created with JWT, so the idea was to find vulnerability for JWT. The simplest exploit was to change algorithm type to none and then we could manipulate the cookie. But when I changed that I received message from server that this algorithm is not allowed, so I was looking for another vulnerability. I was searching for a some time, but without luck and then I found another article describing the none algorithm vulnerability. In this article I noticed that they are creating payload with multiple cases of none e.g. None, nOnE etc. So I tried this one and it worked. I was able to change username to admin, but flag did not appear, so I was trying different usernames, but every time the page was empty. Then I checked the page source code and noticed commented link to js file. In that file was the flag base64 encoded. After solving this, name of this task became obvious. :)\n7 - Welcome, I, you In this task there was information to use endpoint /greeting/\u0026lt;name\u0026gt;. I noticed that in HTTP reply there was Server header with following value: Werkzeug/3.0.1 Python/3.8.18 and the only vulnerability for Python I knew was Jinja Template injection. I tried to send some simple template and see if it is evaluated by server or not. I sent {{ 2 + 2}} and the response was Welcome 4!. Thus I started to looking for payload that gives me the flag and I found this one /greeting/{{request.application.__globals__.__builtins__.__import__('os').popen('cat flag.txt').read()}}\n8 - Why s0 deserious? I liked this task the most. On the page there was information that this page is about deserialization and there is object of class FileRead. They also mentioned that the flag is in file flag.txt and you have to provide date to parameter data in PHP. At first I have no idea what should I do, but I started to looking for how to deserialize in PHP and I found that I have to create a string that represents an object. I used provided information and prepared following payload: ?data=O:8:\u0026quot;FileRead\u0026quot;:1:{s:4:\u0026quot;file\u0026quot;;s:17:\u0026quot;/var/www/flag.txt\u0026quot;;}. In response I got content of file with the flag.\nConclusion I think the tasks were quite easy for someone familiar with CTF\u0026rsquo;s and for me they were just right. I writing this a few days after I solved these tasks and I think I should describe these tasks just after I solved it or even during solving, because I don\u0026rsquo;t remember my exact thought process and what exactly I was trying to do, but it could be also interesting and informative.\n","permalink":"https://flunka.github.io/posts/ctf-challange/","summary":"Intro Recently while browsing the internet I noticed a CTF challenge that was form of preselection to job interview. Generally I was not very interested with job in security field, but I wanted to try if I can solve any of the tasks without any preparation and generally I like challenges. I did some CTF\u0026rsquo;s in the past but it was a couple years ago when I was at the university and I think I wasn\u0026rsquo;t good at it.","title":"CTF Challenge"},{"content":"Introduction Some time ago I\u0026rsquo;ve stared working on web app in Go. I\u0026rsquo;ve been using standard net/http library to handle all requests. I have also implemented some basic logging and then I realized that I\u0026rsquo;ve written quite a bit code that not necessary is related with my app, but it does basic HTTP server stuff. So I\u0026rsquo;ve asked myself: \u0026ldquo;Do I need use some HTTP server like for Python web app? Or can I implement all with net/http Go library. I\u0026rsquo;ve started to looking for some answers and have found many statements that I don\u0026rsquo;t need HTTP server and can implement everything in Go, but most of the times using HTTP sever like nginx or Apache can save me a lot of time and issues. Of course for learning purposes it is good idea to implement all by yourself. You will face many issues and rabbit holes by going this way and it will give you experience and understating, but if you want to deliver some app you can leverage existing solutions that solve all those problems.\nKeeping that in mind I\u0026rsquo;ve decided to learn more about nginx. I want to have better understating what it can provide and how I can leverage it. Currently I have some experience with nginx configuration, but I don\u0026rsquo;t feel confident and I definitely not aware of all its features. The purpose of this post is to document my familiarizing with nginx.\nThese notes are based on free book NGINX Cookbook 2nd edition 2022 which is available on Nginx site\nConfiguration /etc/nginx/nginx.conf is default entry point. Directories site-enabled and site-available are deprecated. Directory conf.d should be used for config files.\nnginx -t - test configuration\nnginx -s realod - reload configuration\nEnable Stub Status To enable stub status use stub_status directive in some location. It gives you information about:\nactive connections total accepted connection connections handled requests served This information is global. It is not specific to HTTP server where stub status is defined. location /stub_status { stub_status; # Set IP restrictions as appropriate allow 127.0.0.1; deny all; } Load balancer Nginx can load balance HTTP, TCP and UDP traffic.\nLoad balancing algorithms Load balancing algorithm are:\nround robin (default) least connections - least_conn least time (only with nginx plus) - least_time generic hash - hash random - random IP hash (only for HTTP) - ip_hash\\ backup server will be used when both primary servers are down. The health of upstream server is passively checked by default by monitoring the response of client requests (max_fails=1 and fail_timeout=10s, so if one request failed, server will be excluded from pool for 10 sec). Active health checks are available with nginx plus.\nHTTP To configure load balancing for HTTP traffic you have to define server pool in upstream context and then redirect traffic to server pool by proxy_pass directive.\nHTTP example upstream backend { server 10.10.12.45:80 weight=1 max_fails=3 fail_timeout=3s; server app.example.com:80 weight=2; server spare.example.com:80 backup; } server { location / { proxy_pass http://backend; } } TCP/UDP To configure load balancing for TCP/UDP traffic you have to define stream context and inside that context sever pool should be defined in upstream context. This configuration can not be added in conf.d directory because this directory is inside http block. You should create stream.conf.d directory and include that directory in main config file.\nTCP/UDP example Main config\nstream { include /etc/nginx/stream.conf.d/*.conf; } stream.conf\nupstream mysql_read { least_conn; server read1.example.com:3306; server read2.example.com:3306; server 10.10.12.34:3306 backup; } server { listen 3306; proxy_pass mysql_read; } upstream ntp { server ntp1.example.com:123 weight=2; server ntp2.example.com:123; } server { listen 123 udp; proxy_pass ntp; } HTTP/2 To configure HTTP/2 you have to use http2 keyword in listen directive. It is possible to use HTTP/2 without SSL, but many clients do not support HTTP/2 without encryption. To use encryption to upstream use grpcs:// protocol.\nHTTP/2 Example upstream grpcservers { server backend1.local:50051; server backend2.local:50051; } server { listen 443 ssl http2 default_server; ssl_certificate server.crt; ssl_certificate_key server.key; location / { grpc_pass grpc://grpcservers; } location = /demo.html { http2_push /style.css; http2_push /image1.jpg; } } Traffic management A/B testing To split request in specified proportion you can use split_clients module. It takes 3 parameters:\nstring for hash output variable proportion mapping A/B testing example This example splits requests to static file. 33.3% percent of users will get file from sitev2 dir and rest of users from sitev1 dir.\nsplit_clients \u0026#34;${remote_addr}\u0026#34; $site_root_folder { 33.3% \u0026#34;/var/www/sitev2/\u0026#34;; * \u0026#34;/var/www/sitev1/\u0026#34;; } server { listen 80 _; root $site_root_folder; location / { index index.html; } } GeoIP Installation apt-get install nginx-module-geoip mkdir /etc/nginx/geoip cd /etc/nginx/geoip wget \u0026#34;http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz\u0026#34; gunzip GeoIP.dat.gz wget \u0026#34;http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz\u0026#34; gunzip GeoLiteCity.dat.gz Usage load_module \u0026#34;/usr/lib64/nginx/modules/ngx_http_geoip_module.so\u0026#34;; http { geoip_country /etc/nginx/geoip/GeoIP.dat; geoip_city /etc/nginx/geoip/GeoLiteCity.dat; # ... } Variables geoip_country and geoip_city give variables that start with $geoip.\nThose variables can be pass to your application or some traffic decisions can be made based on it.\nAll variables are available at nginx site\nLimiting connections To limit HTTP connections you should create zone with limit_conn_zone directive (key is remote address in binary form, zone name is limitbyaddr and its size is 10MB). limit_conn_status specifies response code if limit is reached (503 is default). limit_conn directive takes two parameters: zone name and number of connections. You can use limit_conn in http, server and location context.\nhttp { limit_conn_zone $binary_remote_addr zone=limitbyaddr:10m; limit_conn_status 429; # ... server { # ... limit_conn limitbyaddr 40; # ... } } Limiting rate To limit rate of HTTP requests you can create zone with limit_req_zone directive to set allowed request rate. To limit_req directive you can add two optional parameters burst and delay. burst will allow to exceed rate limit, but then all requests will be rejected. delay specifies how many packets can be made up front without throttling. It can be use in http(to verify), server and location contexts.\nhttp { limit_req_zone $binary_remote_addr zone=limitbyaddr:10m rate=3r/s; limit_req_status 429; # ... server { # ... limit_req zone=limitbyaddr; location / { limit_req zone=limitbyaddr burst=12 delay=9; } # ... } } Limiting bandwidth You can limit bandwidth with limit_rate_after and limit_rate directives. Limiting bandwidth is per connection, so it connection limit should also be configured. It can be used in http, server and location context. Following example limits bandwidth to 1 MB/sec after 10MB.\nlocation /download/ { limit_rate_after 10m; limit_rate 1m; } Caching To create cache for requests you can use proxy_cache_path and specify:\npath to directory that will be created for cache, zone name for shared memory and its size (in example name is CACHE and size is 60MB) (optional) directory structure (levels) (optional) when release cached entries if not requested (inactive) (optional) max size of cache (max_size) proxy_cache_path can be used only in http context.\nTo use created cache you have to use proxy_cache directive in http, server or location context.\nYou can define input for hash with proxy_cache_key directive.\nDuring the tests I noticed that without proxy_cache_valid nginx does not cache at all, so this directive is required. It takes two parameters:\nlist of status codes (optional; if not specified only responses with codes 200, 301 and 302 will be cached) time of how long cache will be valid Cache can be bypassed by using proxy_cache_bypass directive. In example cache for location /live will be bypassed when request contains header cache_bypass.\nTo increase nginx performance you can add header to response to allow client to cache responses. In example all css and js files should cached on client side and expired after one year. ~* modifier after location means that URI should be match for case insensitive regular expression.\nExample http { proxy_cache_path /var/nginx/cache keys_zone=CACHE:60m levels=1:2 inactive=3h max_size=20g; server { proxy_cache CACHE; proxy_cache_key \u0026#34;$host$request_uri $cookie_user\u0026#34;; proxy_cache_valid 200 5m; location /live { proxy_cache_bypass $http_cache_bypass; } location ~* \\.(css|js)$ { expires 1y; add_header Cache-Control \u0026#34;public\u0026#34;; } } } Hardening Basic Authentication Open source version of nginx only allows for basic HTTP authentication. Other methods are implemented in nginx plus.\nTo enable HTTP basic authentication, you have to create file with credentials.\nname1:password1 name2:password2:comment name3:password3 Password have to be encrypted or hashed e.g. You can use openssl command to encrypt password: openssl passwd YourPassword Next you can use auth_basic and auth_basic_user_file to enable HTTP basic authentication. It can be used in http, server and location context.\nlocation / { auth_basic \u0026#34;Private site\u0026#34;; auth_basic_user_file conf.d/passwd; } Authentication Subrequest In the book it is called authentication, but for me it looks like authorization mechanism.\nOpen source nginx allows to authorize requests by third-party system. In order to do that you have to use auth_reqest with internal location. If you want to keep some data from subrequest response you can use auth_request_set.\nTo remove body from subrequest use proxy_pass_request_body directive and change Content-Length header to \u0026quot;\u0026quot;.\nIf subrequest response status is 200, original request will be served.\nIf subrequest response status is 401 or 403, original request will return the same status code.\nlocation /private/ { auth_request /auth; auth_request_set $auth_status $upstream_status; } location = /auth { internal; proxy_pass http://auth-server; proxy_pass_request_body off; proxy_set_header Content-Length \u0026#34;\u0026#34;; proxy_set_header X-Original-URI $request_uri; } Limit by IP You can limit access to specific IP addresses by using allow and deny directives. It can be used in http, server, location and stream context. It also can by combined with other mechanisms e.g. Basic authentication by using satisfy directive. With satisfy any only one criteria has to be met and with satisfy all all criteria have to be met.\nlocation / { satisfy any; allow 192.168.1.0/24; deny all; auth_basic \u0026#34;closed site\u0026#34;; auth_basic_user_file conf/htpasswd; } HTTPS To enable https you have to specify ssl_certificate and ssl_certificate_key.\nOther options also can be adjusted if needed.\nhttp { # All directives used below are also valid in stream server { listen 8443 ssl; # Set accepted protocol and cipher ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # RSA certificate chain loaded from file ssl_certificate /etc/nginx/ssl/example.crt; # RSA encryption key loaded from file ssl_certificate_key /etc/nginx/ssl/example.pem; # Elliptic curve cert from variable value ssl_certificate $ecdsa_cert; # Elliptic curve key as file path variable ssl_certificate_key data:$ecdsa_key_path; # Client-Server negotiation caching ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; } } By default nginx does not verify upstream certificates. To enable verification proxy_ssl_verify directive can be used.\nlocation / { proxy_pass https://upstream.example.com; proxy_ssl_verify on; proxy_ssl_verify_depth 2; proxy_ssl_protocols TLSv1.2; } If you want also listen for HTTP traffic but only serve https you can configure redirection.\nserver { listen 80 default_server; listen [::]:80 default_server; server_name _; return 301 https://$host$request_uri; } We can also instruct the browser to always send https requests by setting HSTS header. add_header Strict-Transport-Security max-age=31536000;\nSecure location You can also limit access to resources by specifying some secret. Then to access that resources you have to generate link. To generate link you have to make MD5 hash with URI (echo -n 'index.htmlmySecret' | openssl md5 -hex) and your secret and then place hash before URI. www.example.com/resources/a53bee08a4bf0bbea978ddf736363a12/index.html. The drawback of this method that hash in not user specific so anyone who get the link can access resource.\nlocation /resources { secure_link_secret mySecret; if ($secure_link = \u0026#34;\u0026#34;) { return 403; } rewrite ^ /secured/$secure_link; } location /secured/ { internal; root /var/www; } Another way secure access is to restrict access with expiration. This time we can specify any variable to create MD5 hash, so it can be user specific.\\\nlocation /resources { root /var/www; secure_link $arg_md5,$arg_expires; secure_link_md5 \u0026#34;$secure_link_expires$uri$remote_addrmySecret\u0026#34;; if ($secure_link = \u0026#34;\u0026#34;) { return 403; } if ($secure_link = \u0026#34;0\u0026#34;) { return 410; } } To generate link we have to create MD5 hash again, but this time in slightly different way. It’s an MD5 hash in binary format, base64-encoded, with plus signs (+) translated to hyphens (-), slashes (/) translated to underscores (_), and equal signs (=) removed.\necho -n \u0026#39;1924905600/resources/index.html127.0.0.1 mySecret\u0026#39; \\ | openssl md5 -binary \\ | openssl base64 \\ | tr +/ -_ \\ | tr -d = MD5 hash and expiration date in the Unix epoch format we have to add in query string /resources/index.html?md5=sqysOw5kMvQBL3j9ODCyoQ\u0026amp;expires=1924905600\nStreaming Serving MP4 and FLV The only thing to enable streaming is to use mp4 or flv directive under desired location.\nhttp { server { # ... location /videos/ { mp4; } location ~ \\.flv$ { flv; } } } Logs Logs format To specify logs format use log_format directive (it is only valid in HTTP context). You have to specify name of format and how logs should looks like. You can define multiple logs formats. To use log format, use access_log directive with log file path and logs format name. access_log directive can be used in many context with different file path and format. Error logs can not have defined format. You can only specify file path and log level (default is error). Available log levels are debug, info, notice, warn, error, crit, alert, or emerg. Logs can be also forwarded to syslog server. To do that use syslog parameter and provide options:\nserver (required) - IP, DNS or Unix socket facility (optional; default: local7) - one of 23 attributes defined in RFC for syslog tag (optional; default: nginx) severity (optional; default: info) nohostname (optional) - disables adding of the hostname filed into the syslog message header Writing logs can be also buffered. buffered parameter specifies buffer size. flush specifies how long log can be in buffer. gzip specifies compression from 1 (lowest compression) to 9 (highest compression). http { log_format geoproxy \u0026#39;[$time_local] $remote_addr \u0026#39; \u0026#39;$realip_remote_addr $remote_user \u0026#39; \u0026#39;$proxy_protocol_server_addr $proxy_protocol_server_port \u0026#39; \u0026#39;$request_method $server_protocol \u0026#39; \u0026#39;$scheme $server_name $uri $status \u0026#39; \u0026#39;$request_time $body_bytes_sent \u0026#39; \u0026#39;$geoip_city_country_code3 $geoip_region \u0026#39; \u0026#39;\u0026#34;$geoip_city\u0026#34; $http_x_forwarded_for \u0026#39; \u0026#39;$upstream_status $upstream_response_time \u0026#39; \u0026#39;\u0026#34;$http_referer\u0026#34; \u0026#34;$http_user_agent\u0026#34;\u0026#39;; server { access_log /var/log/nginx/access.log geoproxy buffer=32k flush=1m gzip=1; error_log /var/log/nginx/error.log warn; } server { error_log syslog:server=10.0.1.42 debug; access_log syslog:server=10.0.1.42,tag=nginx,severity=info geoproxy; } } Tracing $request_id variable is available since nginx version 1.11.0. It can be use to trace requests. It contains randomly generated string of 32 hex characters. In example below this variable is used in nginx logs format and it is also passed to upstream server (proxy_set_header) and it is added to response to client (add_header)\nlog_format trace \u0026#39;$remote_addr - $remote_user [$time_local] \u0026#39; \u0026#39;\u0026#34;$request\u0026#34; $status $body_bytes_sent \u0026#39; \u0026#39;\u0026#34;$http_referer\u0026#34; \u0026#34;$http_user_agent\u0026#34; \u0026#39; \u0026#39;\u0026#34;$http_x_forwarded_for\u0026#34; $request_id\u0026#39;; upstream backend { server 10.0.0.42; } server { listen 80; # Add the header X-Request-ID to the response to the client add_header X-Request-ID $request_id; location / { proxy_pass http://backend; # Send the header X-Request-ID to the application proxy_set_header X-Request-ID $request_id; access_log /var/log/nginx/access_trace.log trace; } } Summary Before reading the NGINX Cookbook 2nd edition 2022 I thought nginx is just a HTTP server. Now I am more aware what nginx can do. I am really impressed how many useful features nginx has e.g. Load balancing, caching. There is also a way more features in nginx plus, but I did not include them here because I don\u0026rsquo;t have environment to test it.\n","permalink":"https://flunka.github.io/posts/getting-familiar-with-nginx/","summary":"Introduction Some time ago I\u0026rsquo;ve stared working on web app in Go. I\u0026rsquo;ve been using standard net/http library to handle all requests. I have also implemented some basic logging and then I realized that I\u0026rsquo;ve written quite a bit code that not necessary is related with my app, but it does basic HTTP server stuff. So I\u0026rsquo;ve asked myself: \u0026ldquo;Do I need use some HTTP server like for Python web app?","title":"Getting Familiar With Nginx"},{"content":"Download qcow image Download qcow image from Ubuntu repo\nResize image qemu-img info ubuntu.img qemu-img resize ubuntu.img 20G #or qemu-img resize ubuntu.img +10G Create user-data and meta-date for cloud-init Users are created on the first boot, so it impossible to change password/ssh key after first boot.\nTo create password hash use following command: mkpasswd --method=SHA-512 --rounds=4096\nuser-data\n#cloud-config users: - name: ubuntu ssh_authorized_keys: - ssh-rsa AAAAB3...asdf root@localhost sudo: \u0026#39;ALL=(ALL) NOPASSWD:ALL\u0026#39; groups: sudo shell: /bin/bash - name: ubuntu2 lock_passwd: false passwd: \u0026lt;password-hash\u0026gt; sudo: \u0026#39;ALL=(ALL) NOPASSWD:ALL\u0026#39; groups: sudo shell: /bin/bash meta-data\nlocal-hostname: my-host Create iso image genisoimage -output cloud-init.iso -volid cidata -joliet -rock user-data meta-data Create network xml Examples can be found here\n\u0026lt;network\u0026gt; \u0026lt;name\u0026gt;default\u0026lt;/name\u0026gt; \u0026lt;bridge name=\u0026#34;virbr0\u0026#34;/\u0026gt; \u0026lt;forward mode=\u0026#34;nat\u0026#34;/\u0026gt; \u0026lt;ip address=\u0026#34;192.168.122.1\u0026#34; netmask=\u0026#34;255.255.255.0\u0026#34;\u0026gt; \u0026lt;dhcp\u0026gt; \u0026lt;range start=\u0026#34;192.168.122.2\u0026#34; end=\u0026#34;192.168.122.254\u0026#34;/\u0026gt; \u0026lt;/dhcp\u0026gt; \u0026lt;/ip\u0026gt; \u0026lt;/network\u0026gt; Create network sudo virsh net-define /path/to/network.xml sudo virsh net-start \u0026lt;network-name\u0026gt; Create simple domain xml Examples can be found here\nTo boot from disk disk type must be defined (driver tag).\n\u0026lt;domain type=\u0026#39;qemu\u0026#39;\u0026gt; \u0026lt;name\u0026gt;nginx-vm\u0026lt;/name\u0026gt; \u0026lt;memory\u0026gt;419200\u0026lt;/memory\u0026gt; \u0026lt;vcpu\u0026gt;2\u0026lt;/vcpu\u0026gt; \u0026lt;os\u0026gt; \u0026lt;type arch=\u0026#39;x86_64\u0026#39; machine=\u0026#39;pc\u0026#39;\u0026gt;hvm\u0026lt;/type\u0026gt; \u0026lt;boot dev=\u0026#34;hd\u0026#34; /\u0026gt; \u0026lt;/os\u0026gt; \u0026lt;devices\u0026gt; \u0026lt;emulator\u0026gt;/usr/bin/qemu-system-x86_64\u0026lt;/emulator\u0026gt; \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;cdrom\u0026#39;\u0026gt; \u0026lt;source file=\u0026#39;/path/to/cloud-init.iso\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;hdc\u0026#39;/\u0026gt; \u0026lt;readonly/\u0026gt; \u0026lt;/disk\u0026gt; \u0026lt;disk type=\u0026#39;file\u0026#39; device=\u0026#39;disk\u0026#39;\u0026gt; \u0026lt;source file=\u0026#39;/path/to/image.qcow\u0026#39;/\u0026gt; \u0026lt;target dev=\u0026#39;hda\u0026#39;/\u0026gt; \u0026lt;driver name=\u0026#39;qemu\u0026#39; type=\u0026#39;qcow2\u0026#39; cache=\u0026#39;none\u0026#39;/\u0026gt; \u0026lt;/disk\u0026gt; \u0026lt;interface type=\u0026#39;network\u0026#39;\u0026gt; \u0026lt;source network=\u0026#39;default\u0026#39;/\u0026gt; \u0026lt;/interface\u0026gt; \u0026lt;serial type=\u0026#39;pty\u0026#39;\u0026gt; \u0026lt;target port=\u0026#39;0\u0026#39;/\u0026gt; \u0026lt;/serial\u0026gt; \u0026lt;console type=\u0026#39;pty\u0026#39;\u0026gt; \u0026lt;target type=\u0026#39;serial\u0026#39; port=\u0026#39;0\u0026#39;/\u0026gt; \u0026lt;/console\u0026gt; \u0026lt;graphics type=\u0026#39;vnc\u0026#39; port=\u0026#39;-1\u0026#39;/\u0026gt; \u0026lt;/devices\u0026gt; \u0026lt;/domain\u0026gt; Create vm sudo virsh define /paht/to/vm.xml sudo virsh start \u0026lt;vm-name\u0026gt; Trobleshooting If you are not able to connect to console (virsh console \u0026lt;vm-name\u0026gt;). You can try to use vncviewer to connect to vm. At /var/log/libvirt/qemu/\u0026lt;vm-name\u0026gt;.log you can find vnc connection details To validate cloud-init config use following commnad: cloud-init schema -c \u0026lt;your-user-data-file\u0026gt; ","permalink":"https://flunka.github.io/posts/create-virsh-ubuntu-vm/","summary":"Download qcow image Download qcow image from Ubuntu repo\nResize image qemu-img info ubuntu.img qemu-img resize ubuntu.img 20G #or qemu-img resize ubuntu.img +10G Create user-data and meta-date for cloud-init Users are created on the first boot, so it impossible to change password/ssh key after first boot.\nTo create password hash use following command: mkpasswd --method=SHA-512 --rounds=4096\nuser-data\n#cloud-config users: - name: ubuntu ssh_authorized_keys: - ssh-rsa AAAAB3...asdf root@localhost sudo: \u0026#39;ALL=(ALL) NOPASSWD:ALL\u0026#39; groups: sudo shell: /bin/bash - name: ubuntu2 lock_passwd: false passwd: \u0026lt;password-hash\u0026gt; sudo: \u0026#39;ALL=(ALL) NOPASSWD:ALL\u0026#39; groups: sudo shell: /bin/bash meta-data","title":"Create Virsh Ubuntu VM"}]