Archive for the ‘Debian’ tag
Dynamic disk partitioning with Debian preseed
Hi folks, this article shows how to create a dynamic disk partitioning scheme using Debian preseed.
TL;DR: I’ve added the partitioning file (and other preseed files) on my GitHub account. feel free to download it if you don’t want to read the entire article.
Overview
Debian preseed provides a way to set answers to questions asked during the installation process, without having to manually enter the answers while the installation is running. This makes it possible to fully automate most types of installations and it even offers some features not available during normal installations. Most of these questions are asked by Debian installer and can be preseeded by setting the answers in the debconf database.
The Debian installer has a directive called early_command. Using it, for example, you can run a Bourne Shell script (it isn’t Bash) or create a sh script inside of it. It’s powerful, but it has a problem. It doesn’t understand multiple lines, I mean, you need to create your rules in just one (logical) line.
PS: To organize and create a beautiful script, I’ve separated it in multiple lines (adding a backslash “\” at the end of each line).
Partitioning rules
The early_command will run immediately before the partitioner starts. It will count the number of disks, define the RAID level and the partitioning scheme. To do that, it will use the following rule:
1 disk = LVM Physical device 2 disks = RAID 1 + LVM Physical device 3 disks = RAID 5 + LVM Physical device 4 disks or more = RAID 10 + LVM Physical device
Partitioning methods
PartMan uses 4 different methods to partition the disks:
- regular – use the usual partition types for your architecture
- lvm – use LVM to partition the disk
- crypto – use LVM within an encrypted partition
- raid – use RAID partition the disk (with or without LVM)
To define the method, use the option “partman-auto/method”, for example:
partman-auto/method "raid"
When the method is defined, the PartMan needs to know what disks will be used in the partitioning scheme. If the system has only one disk the installer will default to using that, but otherwise, the device name must be given in traditional, non-devfs format (so e.g. /dev/hda or /dev/sda, and not e.g. dev/discs/disc0/disc). If the system has two disks (or more), you need to specify all of them:
partman-auto/disk "/dev/sda /dev/sdb /dev/sdc /dev/sdd"
In addition, it’s necessary to specify how the disks will be used in the RAID setup. Remember to use the correct partition numbers for logical partitions and that RAID levels 0, 1, 5, 6 and 10 are supported and the devices are separated using “#”. The parameters are:
<raidtype> <devcount> <sparecount> <fstype> <mountpoint> <devices> <sparedevices>
Using four disks (RAID 10 + LVM), for example, the values are defined as follow:
partman-auto-raid/recipe "10 4 0 lvm - /dev/sda1#/dev/sdb1#/dev/sdc1#/dev/sdd1 ."
Why Sofware RAID?
Perhaps are you thinking why someone would use software instead of using Hardware RAID? The answer is simple: Everything depends on your point of view. Hardware RAID (integrated into the motherboard or a separate controller) is similar in performance to the software RAID. But, it has certain features not available in software RAID and that are never implemented in low-cost controllers, such as caching, hot-swapping, and battery backup. On the other hand, software RAID is implemented by the operating system, it is cheap, easy and a fairly versatile option. However, it has certain limitations, for example, it has no hotswap. In my case, I’m using Software RAID (sometimes) because, I have a few servers with low-cost controllers.
Both of them (Hardware and Software RAID) aren’t perfect. You can still lose the array to a controller failure or operator error.
Partitioning Recipe
After choosing RAID or LVM (or RAID + LVM), it is necessary to define the recipe. You can choose predefined recipes, create your own recipe file and point at it (if you have a way to get it into the environment) or you can create your own recipe in a preseed file. In my case, I’m using just the last option (I won’t describe the others, you can read the PartMan’s documentation to understand all of them).
The recipe is defined by the option “partman-auto/expert_recipe”. Using RAID, you need to specify what devices will be included in the RAID and/or what partitions will be logical volumes. Like I said, the early_command understands just one (logical) line. But, I’ve separated it in multiple lines. Take a look in the code below:
partman-auto/expert_recipe string "multiraid :: \ 0 0 0 raid \ $lvmignore{ } \ $primary { } \ method{ raid } . \
The “multiraid” in the first line is the name of the recipe (it is used by another PartMan option).
Note that second line has the values “0 0 0 raid”, they are respectively the minimum size, priority and maximum size (I’ll talk about that in the next paragraph). The other options are:
- $lvmignore{ } – The RAID partitions are tagged as “lvmignore”, while the LVM logical volumes as “defaultignore” and “lvmok”.
- $primary{ } – used to define the partition as primary.
- method { } – used to define the type of formatting. You can use swap, raid, or format.
Now, take a look in the lines below:
512 40960 15360 ext4 \ $defaultignore{ } \ $lvmok{ } \ lv_name{ lv_root } \ method{ format } \ format{ } \ use_filesystem{ } \ filesystem{ ext4 } \ mountpoint{ / } . \
PS: The “.” in the last line is a separator between hard drives.
Like I said, in Debian preseed files, each partition has a minimum size a priority value and maximum size. As you can see, the first line has the values “512 40960 15360 ext4”. These values are used by PartMan to create a smart partitioning scheme, they mean:
- 512 – the minimum size of partition in Mb.
- 40960 – the priority if it and other listed partitions are vying for space on the disk (this is compared with the priorities of the other partitions).
- 15360 – the maximum size of partition in Mb.
- ext4 – the filesystem type.
The other options used in the previous example are:
- $defaultignore{ } – used to avoid a partition definition so that it is ignored in the default case. That is to say it will be valid in the LVM case.
- $lvmok{ } – used to define a LVM logical volume.
- lv_name{ lv_root } – set the LVM logical volume name.
- method{ format } – set to format the partition. (use “keep” to not format or “swap” for swap partitions).
- format{ } – also needed so the partition will be formatted.
- use_filesystem{ } – this partition will have a filesystem on it (it won’t be swap, LVM, etc)
- filesystem{ ext4 } – what filesystem it gets
- mountpoint{ / } – where it will be mounted
Alternatively, you can define the volume group, if you don’t specify it, the PartMan will use a default name:
partman-auto-lvm/new_vg_name string VolGroup00
Swap size and Partitioning scheme
The SWAP size will be calculated based on the amount of RAM and I’ve used the following rule:
if RAM < 2GB then SWAP = 2x physical RAM if RAM > 2GB or MEM < 8GB then SWAP = Equal to the amount of RAM if RAM > 8GB then SWAP = At least 4 GB
And the partitioning scheme will be as follows:
/dev/mapper/VolGroup00/lv_root - 512Mb to 15Gb - / ext4 /dev/mapper/VolGroup00/lv_tmp - 256Mb to 4Gb - /tmp ext4 /dev/mapper/VolGroup00/lv_var - 512Mb to 15Gb - /var ext4 /dev/mapper/VolGroup00/lv_delete - all remaining space
Did you understand why I have created a logical volume called lv_delete? Of course not right? So, the Debian installer has a bug. I don’t know why it allocates all remaining space to the last logical volume (just when you’re using preseed files). Because of that, I’ve defined the lv_delete and before the install finishes, it will be deleted, giving back this space to the volume group.
Preseed file
The following preseed file is used in my production environment. Feel free to use it or change it if you want:
### Partitioning # # Use RAID+LVM or just LVM to partition the disk # Partitioning scheme: # # /dev/md0 - RAID device / LVM Physical volume (if you have 2 or more disks) # /dev/mapper/VolGroup00/lv_swap - the swap is calculated over the amount # of RAM in the system, ex: # if RAM < 2GB then SWAP = 2x physical RAM # if RAM > 2GB or MEM < 8GB then SWAP = Equal to the amount of RAM # if RAM > 8GB then SWAP = At least 4 GB # /dev/mapper/VolGroup00/lv_root - 512Mb to 15Gb - / ext4 # /dev/mapper/VolGroup00/lv_tmp - 256Mb to 4Gb - /tmp ext4 # /dev/mapper/VolGroup00/lv_var - 512Mb to 15Gb - /var ext4 # /dev/mapper/VolGroup00/lv_delete - all remaining space. This logical # volume was created because the Debian installer has a bug. It allocates # all remaining space to the last logical volume. The late_command will # execute a lvremove VolGroup00/lv_delete and give back this space to the # volume group. # The options used in the partitioning can be found in the following url: # https://wikitech.wikimedia.org/wiki/PartMan # These commands will run immediately before the partitioner starts. d-i partman/early_command string \ COUNT=0; \ SPARE=0; \ for DISK in $(list-devices disk); do \ DISKS="${DISKS} ${DISK}"; \ if [ "$(echo ${DISK} | cut -d'/' -f3)" = "cciss" ]; then \ DEVS="${DEVS}${DISK}p1#"; \ SPARE_DEV="${DISK}p1"; \ else \ DEVS="${DEVS}${DISK}1#"; \ SPARE_DEV="${DISK}1"; \ fi; \ COUNT=$((COUNT + 1)); \ done; \ DISKS=$(echo ${DISKS} | sed "s/^ //g"); \ DEVS=$(echo ${DEVS} | sed "s/#$//g"); \ if [ "${COUNT}" -eq "1" ]; then \ RAID="-1"; \ elif [ "${COUNT}" -eq "2" ]; then \ RAID="1"; \ elif [ "${COUNT}" -eq "3" ]; then \ RAID="5"; \ elif [ "${COUNT}" -ge "4" ]; then \ RAID="10"; \ SPARE=$((COUNT % 2));\ fi; \ if [ ${SPARE} -eq "1" ]; then \ COUNT=$((COUNT - 1)); \ DEVS=$(echo ${DEVS} | sed -e "s|${SPARE_DEV}||g;s/#$//g"); \ else \ SPARE_DEV=""; \ fi; \ MEM=$(($(sed -n 's/^MemTotal: \+\([0-9]*\) kB/\1/p' /proc/meminfo) / 1024)); \ if [ "${MEM}" -lt "2048" ]; then \ SWAP=$((MEM * 2)); \ elif [ "${MEM}" -gt "2048" ] || [ "${MEM}" -le "8192" ]; then \ SWAP=${MEM}; \ elif [ "${MEM}" -ge "8192" ]; then \ SWAP=4096; \ fi; \ debconf-set partman-auto/disk "$DISKS"; \ if [ "${RAID}" -ge "1" ]; then \ debconf-set partman-auto/method "raid"; \ debconf-set partman-auto-raid/recipe "${RAID} ${COUNT} ${SPARE} lvm - ${DEVS} ${SPARE_DEV} ."; \ debconf-set partman-auto/expert_recipe "multiraid :: \ 0 0 0 raid \ \$lvmignore{ } \ \$primary { } \ method{ raid } . \ 256 256 ${SWAP} linux-swap \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_swap } \ method{ swap } \ format{ } . \ 512 40960 15360 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_root } \ method{ format } \ format{ } \ use_filesystem{ } \ filesystem{ ext4 } \ mountpoint{ / } . \ 256 5120 4096 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_tmp } \ method{ format } \ format{ } \ use_filesystem{ } \ filesystem{ ext4 } \ mountpoint{ /tmp } . \ 512 40960 15360 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_var } \ method{ format } \ format{ } \ use_filesystem{ } \ filesystem{ ext4 } \ mountpoint{ /var } . \ 1024 1024 1024 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_delete } ."; \ else \ debconf-set partman-auto/method "lvm"; \ debconf-set partman-auto/expert_recipe "root :: \ 256 256 ${SWAP} linux-swap \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_swap } \ method{ swap } \ format{ } . \ 512 40960 15360 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_root } \ method{ format } \ format{ } \ use_filesystem{ } \ filesystem{ ext4 } \ mountpoint{ / } . \ 256 5120 4096 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_tmp } \ method{ format } \ format{ } \ use_filesystem{ } \ filesystem{ ext4 } \ mountpoint{ /tmp } . \ 512 40960 15360 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_var } \ method{ format } \ format{ } \ use_filesystem{ } \ filesystem{ ext4 } \ mountpoint{ /var } . \ 1024 1024 1024 ext4 \ \$defaultignore{ } \ \$lvmok{ } \ lv_name{ lv_delete } ."; \ fi # Install grub in the first device (assuming it is not a USB stick) d-i grub-installer/bootdev string default # Continue installation without /boot partition? d-i partman-auto-lvm/no_boot boolean true # Name of the volume group for the new system d-i partman-auto-lvm/new_vg_name string VolGroup00 # Remove existing software RAID partitions? d-i partman-md/device_remove_md boolean true # Remove existing logical volume data? d-i partman-lvm/device_remove_lvm boolean true # Unable to automatically remove LVM data d-i partman-lvm/device_remove_lvm_span boolean true # Dummy template for preseeding unavailable questions d-i partman-auto/purge_lvm_from_device boolean true # Write the changes to the storage devices and configure RAID? d-i partman-md/confirm boolean true d-i partman-md/confirm_nooverwrite boolean true # Write the changes to disks and configure LVM? d-i partman-lvm/confirm boolean true d-i partman-lvm/confirm_nooverwrite boolean true # Write the changes to disks? d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true # Finish partitioning and write changes to disk d-i partman/choose_partition select finish # This command is run just before the install finishes, and, # it will remove the lv_delete d-i preseed/late_command string lvremove -f /dev/VolGroup00/lv_delete > /dev/null 2>&1
The last line in the preseed file is the late_command. It works similarily to early_command, the only difference between them is the late_command runs just before the install finishes. In my case, it will remove the lv_delete, but you can use it to do other tasks, such as, for example, install packages or customize the environment before the first boot.
Final Thoughts
Probably you know that nothing is perfect, right? Debian preseed isn’t different. Its syntax isn’t easy (annoying is the right word), it has a weird bug (like I described previously) and its documentation is horrible (Debian users, I’m so sorry about that, but the Kickstart’s documentation is easier and more helpful). Besides, you need to be careful with it (especially with the early_command and the late_command), because, if you forget a minimal detail, the Debian installer will generate a generic error.
So, that is all for now folks. If you have any questions about this article, feel free to ask me.
NginX packages with third-party modules for CentOS and Debian
Hi folks, finally a new post after a couple weeks without one (I was a little bit busy). Now, I want to share with you the NginX packages (built with third-party modules) for CentOS and Debian. The third-party modules included in the packages are:
– cache-purge
– dav-ext
– development-kit
– echo
– fancyindex
– headers-more
– http-auth-pam
– http-substitutions-filter
– lua
– sticky
– upload-progress
– upstream-fair
You can download them from my personal repositories or you can use the source from github to build their own versions.
Feel free to ask questions or make suggestions. That is all for now.
How to get an A+ on Qualys SSL Labs with NginX
Hi Folks, in this post I’ll show you how to configure your SSL Virtual Host to get an A+ on SSL Labs.
SSL and security issues, in general, are complicated subjects and I won’t describe all SSL aspects, just a few configurations to have the perfect environment with NginX.
The pre-requisites to get an A+ on SSL Labs is to have OpenSSL 1.0 or later. It’s necessary because you need to use TLS 1.2 and your NginX package must be built with it. If you are running the latest version of Debian, Ubuntu or CentOS, you’re already using OpenSSL 1.0.
PS: If you want to use the latest NginX build (1.9.3) for CentOS or Debian, click here.
Continuing with the article, let’s talk about protocols. SSLv2 and SSLv3 are insecure protocols, as every System Administrator knows (or at least should know). SSLv2 doesn’t provide a sufficiently high level of security and it’s deprecated in RFC 6176. On the other hand, SSLv3 was killed by the POODLE attack and it’s deprecated in RFC 7568. Probably, these two arguments are enough for you disable both of them completely.
PS: Disabling SSLv2 and SSLv3 won’t let old browsers like IE6 running on Windows XP access your website, because they don’t support TLS.
Within your SSL virtual host, change the ssl_protocols line to enable the TLS protocols as follows:
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
With ssl_protocols configured properly, you will configure the ssl_ciphers. The default ciphers suites used by default by the NginX installation (1.0.5 or later), are enough for an excellent compatibility and security:
ssl_ciphers "HIGH:!aNULL:!MD5";
Or if you want to use a custom line with better performance (it will reduce CPU usage) but with less compatibility:
ssl_ciphers "HIGH:!aNULL:!MD5:!DHE-RSA-AES128-SHA256:!DHE-RSA-AES128-SHA:!DHE-RSA-AES128-GCM-SHA256:!DHE-RSA-AES256-GCM-SHA384:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES256-SHA:!DHE-RSA-CAMELLIA256-SHA:!DHE-RSA-DES-CBC3-SHA:!DHE-RSA-CAMELLIA128-SHA";
PS: To see the complete ciphers suites list, click here.
For the configs above to work properly, I mean, for the server ciphers to be preferred over client ciphers, when using the TLS protocols, enable the following option:
ssl_prefer_server_ciphers on;
In addition, you need to configure a few extra steps. They aren’t related with security directly, but with HTTPS performance.
The ssl_session_cache will configure a cache shared between all worker processes. The cache size is specified in bytes and one megabyte can store about 4000 sessions. The ssl_session_timeout specifies a time during which a client may reuse the session parameters stored in cache. To minimize the TTFB (Time To First Byte), it’s beneficial to set a smaller value for ssl_buffer_size .
To configure them, add or modify the lines (if you already have them) as follows:
ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_buffer_size 4k;
It’s time to talk about certificates. Invalid certificates can cause the browser to display warning messages and stop talking with the website. Using a valid certificate is an essential step and sometimes, even using it, you can get warning messages because the certificate signature algorithm is SHA-1. To fix this problem, basically, you need to replace SHA-1 with SHA-2 certificates. To do that, you need to generate a new CSR and request the Certificate Authorities to sign a new certificate with SHA256. SSL Labs or this website can check if your website is using SHA-1.
PS: Don’t forget, you need to get the intermediate CA certificates signed with SHA256 as well.
With the new certificates (if you aren’t using them yet), sometimes it’s necessary to configure the ssl_verify_depth. It sets the verification depth in the client certificates chain.
ssl_verify_depth 3;
Diffie-Hellman is an asymmetric algorithm used by a lot of protocols, including IPSec, SSL, SSH, PGP and other PKI systems. It provides the capability for two communicating parties to agree upon a shared secret key between them. Also, it is an algorithm created to address the issue of secure encrypted keys from being attacked over the internet when in transmission.
The NginX relies on OpenSSL and uses the default Diffie-Hellman DHE key, which includes a 1024-bit key. The problem is, if you are using a Diffie-Hellman primes smaller than 1024-bit, your system is vulnerable to the Logjam Attack. To fix this problem, you need to generate a stronger DHE parameter. I recommend at least a 2048-bit key. The way to generate it is:
mkdir /etc/nginx/ssl openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
And configure the NginX to use it:
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
HSTS (HTTP Strict Transport Security) is an IETF standards tracking protocol and it is specified in RFC 6797. It is a web security policy mechanism, which is necessary to protect secure HTTPS websites against downgrade attacks, and it greatly simplifies protection against cookie hijacking. Basically, it tells the browser that the website should only be accessed through a secure connection (SSL), instead of using HTTP.
If you access the HTTPS version of the site, the HSTS header will remember this option when trying to access the same site using HTTP and it will automatically switch to HTTPS, provided that the max-age parameter has not expired.
The HSTS Policy is communicated by the server to the user agent via an HTTP response header field named “Strict-Transport-Security”.
To configure HSTS, add the line below to your SSL Virtual Host as follows:
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
The option above tells the web browser to always use SSL to access this website for the next 365 days. More information about HSTS, can be found here.
And now (finally) add the Mod SPDY. SPDY (pronounced speedy) isn’t a security protocol, but an open network protocol developed primarily at Google for transporting web content. It works over SSL/TLS and it manipulates HTTPS traffic with the particular goals of reducing web page load latency and improving web security. It’s the precursor of HTTP2.
PS: The NginX team plans to release new versions of both NginX and NginX Plus by the end of 2015 and they will include support for HTTP/2.
To verify if your NginX package has SPDY support enabled, try this:
# nginx -V 2>&1 | sed -e 's/_module /_module\n/g' | grep spdy
If the command above shows the output “–with-http_spdy_module”, your NginX is ok. If not, you need to install a NginX build with SPDY support. If you are running the latest versions of Debian, Ubuntu or CentOS, you already have SPDY support.
With the SPDY module enabled, let’s configure the SSL virtual host to use it. You just need to change your virtual host from:
listen 443 ssl;
to:
listen 443 ssl spdy; spdy_headers_comp 3; add_header Alternate-Protocol 443:npn-spdy/3;
and reload your NginX:
# /etc/init.d/nginx reload
Now with SPDY working correctly, modern browsers will get your site content over SPDY and old browsers will use normal SSL. If you want to check if your site is using SPDY, use the SPDYCheck.org. If you are using Google Chrome, there is an extension that checks if the website your are visiting has the SPDY protocol enabled.
Another good practice is to redirect all HTTP traffic through your SSL virtual host. I mean, accessing this website via HTTP will automatically redirect the user to access the website via SSL/TLS and SPDY. To do that, modify your HTTP virtual host as follows:
server { listen 80; server_name yodaime.claudioborges.org; return 301 https://$server_name$request_uri; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log notice; }
My HTTPS virtual host is:
server { listen 443 ssl spdy; root /srv/default/www; index index.php index.html; spdy_headers_comp 3; add_header Alternate-Protocol 443:npn-spdy/3; add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;"; server_name yodaime.claudioborges.org; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error.log notice; ssl on; ssl_certificate /etc/nginx/ssl/claudioborges_org.pem; ssl_certificate_key /etc/nginx/ssl/claudioborges_org.key; ssl_verify_depth 3; ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers "HIGH:!aNULL:!MD5"; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_buffer_size 4k; location / { try_files $uri $uri/ =404; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_intercept_errors on; } location = /favicon.ico { access_log off; log_not_found off; } }
That is all for now folks. If you have any questions about this article, feel free to ask me.