Debian 10.3 Released

Debian Linux

Pretty cool! I almost missed that Debian 10.3 got released last week. This is a corrective release, meaning it’s about improving stability and security rather than about introducing major innovations.

Upgrade Debian 10.2 to 10.3

I only have one dedicated server running Debian 10, and will possibly reinstall even that – turns out I’m much more used to CentOS servers than anything else.

BUT this server is still there, so why not upgrade it?

Step 1: Update Debian repositories

First, we run apt-get update. I never noticed it before, but apparently this command is clever enough to recognize that InRelease changes version from 10.2 to 10.3 (see the last line of the output):

root@srv:~ # apt-get update
 Get:1 http://mirrors.online.net/debian buster InRelease [122 kB]
 Get:2 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
 Get:3 https://download.docker.com/linux/ubuntu bionic InRelease [64.4 kB]
 Ign:4 http://mirrors.online.net/debian buster/non-free Sources
 Ign:5 http://mirrors.online.net/debian buster/main Sources
 Ign:6 http://mirrors.online.net/debian buster/main amd64 Packages
 Ign:7 http://mirrors.online.net/debian buster/main Translation-en
 Ign:8 http://mirrors.online.net/debian buster/non-free amd64 Packages
 Ign:9 http://mirrors.online.net/debian buster/non-free Translation-en
 Get:4 http://mirrors.online.net/debian buster/non-free Sources [86.3 kB]
 Get:5 http://mirrors.online.net/debian buster/main Sources [7,832 kB]
 Get:10 http://security.debian.org/debian-security buster/updates/main Sources [102 kB]
 Get:11 http://security.debian.org/debian-security buster/updates/main amd64 Packages [176 kB]
 Get:12 http://security.debian.org/debian-security buster/updates/main Translation-en [92.8 kB]
 Get:6 http://mirrors.online.net/debian buster/main amd64 Packages [7,907 kB]
 Get:7 http://mirrors.online.net/debian buster/main Translation-en [5,970 kB]
 Get:8 http://mirrors.online.net/debian buster/non-free amd64 Packages [88.0 kB]
 Get:9 http://mirrors.online.net/debian buster/non-free Translation-en [88.7 kB]
 Fetched 22.6 MB in 3s (6,828 kB/s)
 Reading package lists… Done
 N: Repository 'http://mirrors.online.net/debian buster InRelease' changed its 'Version' value from '10.2' to '10.3'

Step 2: Upgrade packages and Debian distro

apt-get dist-upgrade brings all the packages to the current release of your Debian/Ubuntu distro. In my case,

root@srv:~ # apt-get dist-upgrade
 Reading package lists… Done
 Building dependency tree
 Reading state information… Done
 Calculating upgrade… Done
 The following NEW packages will be installed:
   linux-headers-4.19.0-8-amd64 linux-headers-4.19.0-8-common linux-image-4.19.0-8-amd64
 The following packages will be upgraded:
   base-files e2fsprogs git-man libboost-iostreams1.67.0 libboost-system1.67.0 libcom-err2 libcups2 libcupsimage2 libext2fs2 libgnutls30 libidn2-0
   libnss-systemd libopenjp2-7 libpam-systemd libpython3.7 libpython3.7-dev libpython3.7-minimal libpython3.7-stdlib libsasl2-2 libsasl2-modules
   libsasl2-modules-db libss2 libsystemd0 libtiff5 libtimedate-perl libudev1 linux-compiler-gcc-8-x86 linux-headers-amd64 linux-image-amd64 linux-kbuild-4.19
   linux-libc-dev openssh-client openssh-server openssh-sftp-server python-apt python-apt-common python3-apt python3.7 python3.7-dev python3.7-minimal sudo
   systemd systemd-sysv udev
 44 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
 Need to get 129 MB of archives.
 After this operation, 325 MB of additional disk space will be used.
 Do you want to continue? [Y/n] y
...

Step 3: Reboot (when convenient)

You don’t have to reboot immediately. The biggest reason to do it is to start using new version of Linux kernel, but there’s hardly a specific update in minor kernel upgrade that justifies immediate downtime.

Here’s the kernel version before reboot:

root@srv:~ # uname -a
Linux srv.ts.fm 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u2 (2019-11-11) x86_64 GNU/Linux

When possible, you should do a graceful reboot:

root@srv:~ # shutdown -r now

After system is back online, we can see that it’s running Debian Buster 10.3 now:

greys@srv:~ $ uname -a
Linux srv.ts.fm 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64 GNU/Linux

See Also




Systemd Startup Times with systemd-analyze

systemd-analyze

Systemd had the goal of optimising startup times from the very beginning. It is therefore no particular wonder that it has a tool for precise startup timing and analysis: systemd-analyze.

Systemd Startup Analysis

Run without command line parameters, systemd-analyze shows a summary of how quickly your system booted Linux last time:

greys@srv:~ $ systemd-analyze
Startup finished in 2.743s (kernel) + 2.654s (userspace) = 5.397s
graphical.target reached after 2.641s in userspace

See What Systemd Service Took Longest to Start

It gets better! You can specify the blame parameter, and systemd-analyze will show you exactly how long each of the startup tasks has taken – that’s really useful for troubleshooting:

greys@srv:~ $ systemd-analyze blame
1.983s docker.service
277ms certbot.service
276ms man-db.service
223ms dev-md0.device
210ms apt-daily-upgrade.service
208ms apt-daily.service
194ms logrotate.service
101ms systemd-fsck@dev-disk-by\x2duuid-cea13f85\x2d61fa\x2d414f\x2d9c2f\x2d1e48ec41ad25.service
69ms chrony.service
66ms systemd-journald.service
61ms ssh.service
59ms systemd-remount-fs.service
57ms systemd-fsck@dev-disk-by\x2duuid-cfceff76\x2df739\x2d49e2\x2da4d1\x2d02472e5457f9.service
46ms systemd-udev-trigger.service
41ms keyboard-setup.service
41ms systemd-logind.service
40ms containerd.service
36ms networking.service
31ms apparmor.service
27ms [email protected]
21ms systemd-tmpfiles-setup.service
21ms rsyslog.service
19ms systemd-update-utmp.service
15ms storage.mount
14ms var-log.mount
14ms systemd-udevd.service
12ms dev-disk-by\x2duuid-799ad160\x2d8a59\x2d4c80\x2db78a\x2d7e3986876964.swap
11ms systemd-user-sessions.service
10ms dev-disk-by\x2duuid-261bd6ac\x2d2f4c\x2d475b\x2da4e4\x2db2548368e0fa.swap
10ms systemd-update-utmp-runlevel.service
9ms systemd-journal-flush.service
9ms polkit.service
9ms dev-disk-by\x2duuid-d45708da\x2d06f4\x2d41d6\x2daabe\x2decd87fb5edbe.swap
8ms systemd-tmpfiles-clean.service
8ms [email protected]
7ms dev-mqueue.mount
7ms sys-kernel-debug.mount
7ms systemd-tmpfiles-setup-dev.service
6ms systemd-modules-load.service
6ms console-setup.service
6ms systemd-sysusers.service
4ms systemd-sysctl.service
4ms kmod-static-nodes.service
4ms dev-hugepages.mount
4ms systemd-random-seed.service
2ms ifupdown-pre.service
1ms docker.socket
greys@srv:~ $

See Also




Project: Password Protect a Website with nginx

nginx
nginx logo

I needed to keep a few older websites online for a short little while, but didn’t want to leave them wide open in case older CMS systems were vulnerable – so I decided to protect them with password.

What is nginx?

nginx (pronounced Engine-Ex) is a webserver, reverse-proxy and caching solution powering a massive portion of the Internet websites today. It’s a lightweight web-server with non-locking implementation, meaning it can server impressive amounts of traffic with humble resource requirements.

nginx was acquired by F5 in 2019.

I’ll be writing a lot more about nginx in 2020, simply because I’m finally catching up with my dedicated hosts infrastructure and will be getting the time to document my setup and best practices.

Password Protecting in nginx

There’s a few steps to protecting a website using nginx (steps are similar but implemented differently in Apache web server):

  1. Decide and create/update the passwords file
  2. Decide on the username and password
  3. Generate password hash and add entry to the passwords file
  4. Update webserver configuration to specify password protection

Because websites are configured as directory locations, you have a choice of protecting the whole website like www.unixtutorial.org or just a part (subdirectory) of it, like www.unixtutorial.org/images.

INTERESTING: even though it’s commonly referred to as password protecting websites, what actually happens is you protect with username and password. So when you’re trying to open a protected website, you get a prompt like this, right there in your browser:

Password protection prompt

Password file and username/password Configuration

Most of the time website access is controlled by files named htpasswd. You either create default password file in /etc/nginx/htpasswd location, or create a website specific version like /etc/nginx/unixtutorial.htpasswd.

You can create a file using touch command:

# touch /etc/nginx/unixtutorial.htpasswd

Or better yet, use the htpasswd command to do it. But because htpasswd is part of Apache tools, you may have to install it first:

$ sudo yum install httpd-tools

When you run the htpasswd command, you specify two parameters: the password file name and the username you’ll use for access.

If the password file is missing, you’ll be notified like this:

$ sudo htpasswd /etc/nginx/htpasswd unixtutorial 
htpasswd: cannot modify file /etc/nginx/htpasswd; use '-c' to create it.

And yes, adding the -c option will get the file created:

$ sudo htpasswd -c /etc/nginx/htpasswd unixtutorial
New password:
Re-type new password:
Adding password for user unixtutorial

Now, if we cat the file, it will show the unixtutorial user and the password hash for it:

$ cat /etc/nginx/htpasswd
unixtutorial:$apr1$bExTryjo/$uxRop/uv5UwXvWl4EM5gv0

IMPORTANT: although this file doesn’t contain actual passwords, only their encrypted hashes, it can still be used to guess your passwords on powerful systems – so take the usual measures to protect access to this file.

Update Website Configuration with Password Protection

I’ve got the following setup for this old website in my example:

server {
     listen      *:80;
     server_name forum.reys.net;
     keepalive_timeout    60;

     access_log /var/log/nginx/forum.reys.net/access.log multi_vhost;
     error_log /var/log/nginx/forum.reys.net/error.log;

location / {
     include "/etc/nginx/includes/gzip.conf";
     proxy_pass  http://172.31.31.47:80;

     include "/etc/nginx/includes/proxy.conf";
     include "/etc/nginx/includes/headers.conf";
     }
}

Protection is done on the location level. In this example, location / means my whole website is protected.

So right in front of the proxy_pass entry, I’ll add my password protection part:

auth_basic "Restricted";
auth_basic_user_file /etc/nginx/htpasswd;

As you can see, we’re referring to the password file that we created earlier. The auth_basic “Restricted” part helps you to configure a specific message (instead of word Restricted) that will be shown during username/password prompt.

That’s how the password protected part will look:

location / {
     include "/etc/nginx/includes/gzip.conf";
     proxy_pass  http://172.31.31.47:80;

     auth_basic "Restricted";
     auth_basic_user_file /etc/nginx/htpasswd;

     include "/etc/nginx/includes/proxy.conf";
     include "/etc/nginx/includes/headers.conf";
     }

Save the file and restart nginx:

$ sudo service restart nginx

Now the website https://forum.reys.net is password protected!

See Also




Host Key Verification Failed

Host key verification failed

When reinstalling servers with new versions of operating system or simply reprovisioning VMs under the same hostname, you eventually get this Host Key Verification Failed scenario. Should be easy enough to fix, once you’re positive that’s a valid infrastructure change.

Host Key Verification

Host key verification happens when you attempt to access remote server with SSH. Before verifying if you have a user on the remote server and whether your password or SSH key match that remote user, SSH client must do basic sanity checks on the lower level.

Specifically, SSH client checks if you attempted connecting to the remote server before. And whether anything changed since last time (it shouldn’t have).

Server (host) keys must not change during a normal life cycle of a server – they are generated at server/VM build stage (when OpenSSH starts up the first time) and remain the same – it’s the server’s identity.

This means if your SSH client has one keyprint for a particular server, and then suddenly detects it’s a different one – it’s flagged as an issue: at best, you’re looking at the new, legit server replacement with the same hostname. At worst, someone’s trying to intercept your connection and/or pretend to be your server.



Host Key Verification Failed

Here’s how I get this error on my Macbook (s1.unixtutorial.org doesn’t really exist, it’s just a hostname I show here as example):

greys@maverick:~ $ ssh s1.unixtutorial.org
Warning: the ECDSA host key for 's1.unixtutorial.org' differs from the key for the IP address '51.159.18.142'
Offending key for IP in /Users/greys/.ssh/known_hosts:590
Matching host key in /Users/greys/.ssh/known_hosts:592
Are you sure you want to continue connecting (yes/no)?

At this stage your default answer should always be “no”, followed by inspection of the known_hosts file to confirm what happened and why identity appears to be different.

If you answer no, you’ll get the Host Key Verification Failed error:

greys@maverick:~ $ ssh s1.unixtutorial.org
Warning: the ECDSA host key for 's1.unixtutorial.org' differs from the key for the IP address '51.159.18.142'
Offending key for IP in /Users/greys/.ssh/known_hosts:590
Matching host key in /Users/greys/.ssh/known_hosts:592
Are you sure you want to continue connecting (yes/no)? no
Host key verification failed.

How To Solve Host Key Verification Errors

The output above actually tells you what to do: inspect file known_hosts and look at the lines 590 and 592 specifically. One of them is likely to be obsolete, and if you remove it the issue will go away.

Specifically, if you (like me) just reinstalled the dedicated server or VM with a new OS but kept the original hostname, then the issue is expected (new server definitely generated a new host key), so the solution is indeed to remove old key from the known_hosts file and re-attempt the connection.

First, I edited the /Users/greys/.ssh/known_hosts file and removed the line 590, which looked something like this. We simply need to find the line with given number, or look for the hostname we just tried to ssh into (s1.unixtutorial.org in my case):

s1.unixtutorial.org,51.159.xx.yy ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlkzdHAyNTYAAAAxyzAgBPbBCXCL5w8

We can try reconnecting now, answer yes and connect to the server:

greys@maverick:~ $ ssh s1.unixtutorial.org
The authenticity of host 's1.unixtutorial.org (51.159.xx.yy)' can't be established.
ECDSA key fingerprint is SHA256:tviW39xN2M+4eZOUGi8UFvBZoHKaLaijBA581Nrhjac.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 's1.unixtutorial.org,51.159.xx.yy' (ECDSA) to the list of known hosts.
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Fri Feb  7 21:18:35 2020 from unixtutorial.org
[greys@s1 ~]$

As you can see, the output now makes a lot more sense: our SSH client can’t establish authenticity of the remote server s1.unixtutorial.org – this is because we removed any mention of that server from our known_hosts file in previous step. Answering yes adds info about s1.unixtutorial.org, so any later SSH sessions will work just fine:

greys@maverick:~ $ ssh s1.unixtutorial.org
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Sat Feb  8 18:31:39 2020 from 93.107.36.193
[greys@s1 ~]$

Copying Host Keys to New Server

I should note that in some cases your setup or organisation would require the same host keys to be kept even with server reinstall. In this case, you’ll need to use last know backup of old server to grab SSH host keys from, to re-deploy them onto the new server – I’ll show/explain this in one of the future posts.

See Also




Resume Downloads with curl

Resuming downloads with curl

If you’re on an unstable WiFi hotspot or simply forgot about a curl download and shut down your laptop, there’s a really cool thing to try: resume download from where you left off.

How To Download a File with curl

I’m downloading the Solus 4.1 release – it’s a 1.7GB ISO image. Here’s the command line that makes curl download the file and put it into local file (note the -O option):

greys@xps:~/Downloads/try $ curl -O http://solus.veatnet.de/iso/images/4.1/Solus-4.1-Budgie.iso  

How To Stop Download with curl

Now, if we press Ctlr+C to stop download, we’ll end up with a partially downloaded file:

greys@xps:~/Downloads $ curl -O http://solus.veatnet.de/iso/images/4.1/Solus-4.1-Budgie.iso
   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                         
                                  Dload  Upload   Total   Spent    Left  Speed                                           
   3 1692M    3 58.8M    0     0  2161k      0  0:13:21  0:00:27  0:12:54 4638k^C  
greys@xps:~/Downloads $ ls -la Solus*iso
-rw-r--r-- 1 greys 102318080 Feb 7 22:58 Solus-4.1-Budgie.iso
greys@xps:~/Downloads $ du -sh Solus-4.1-Budgie.iso
98M Solus-4.1-Budgie.iso

How To Resume Download with curl

To resume download (rather than restart and begin downloading the whole file again0, simply use the -C option. It’s meant to be taking a specific offset in bytes, but also works if you specify “-“, when curl looks at existing file and decides what the offset should be automatically:

That’s it, you can let the download complete now:

greys@xps:~/Downloads $ curl -C - -O http://solus.veatnet.de/iso/images/4.1/Solus-4.1-Budgie.iso                        

** Resuming transfer from byte position 102318080                                                                       

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                         

                                 Dload  Upload   Total   Spent    Left  Speed                                           

100 1595M  100 1595M    0     0  2868k      0  0:09:29  0:09:29 --:--:-- 1766k         

Here’s my resulting file:

-rw-r--r-- 1 greys 1774911488 Feb  7 23:08 Solus-4.1-Budgie.iso

Don’t Forget to Compare File Checksum!

Checking a checksum for newly downloaded ISO image is always a good practice, but it becomes a must when you’re resuming downloads: in addition to ensuring you got the same ISO image software distributors intended, you’re getting the assurance that your resumed download file is intact and fully operational.

I have downloaded Solus 4.1 SHA256 checksum from the same Solus Downloads page, and will use the sha256sum command to generate checksum for the ISO file. Obviously, both checksums must match:

greys@xps:~/Downloads $ cat Solus-4.1-Budgie.iso.sha256sum 
4bf00f2f50e7024a71058c50c24a5706f9c18f618c013b8a819db33482577d17  Solus-4.1-Budgie.iso
greys@xps:~/Downloads $ sha256sum Solus-4.1-Budgie.iso
4bf00f2f50e7024a71058c50c24a5706f9c18f618c013b8a819db33482577d17  Solus-4.1-Budgie.iso

That’s it for today! I can’t wait to try Solus 4.1, will posh about it shortly.

See Also




Secure Keyboard Input in bash Scripts

Secure keyboard input in bash

I needed to create a very simple bash script that required a password to be provided using keyboard and then used further in the script. Turns out, there’s just a way to do it with the bash built-in function called read.



Standard user input in bash

Here’s how a normal user input works: you invoke read function, pass it a variable name. A user is prompted for input by the bash script, and when input is provided it’s shown (echoed) back into terminal – so you can see what you type.

First, let’s create the script:

$ vi input.sh

this will be the content for our file:

#!/bin/bash
echo "Type your password, please:"
read PASS
echo "You just typed: $PASS"

Save the file (press Esc, then type :wq) and make it executable:

$ chmod a+rx input.sh

Now we can run the script and see how it works:

$ ./input.sh
Type your password, please:
mypass
You just typed: mypass

Works great, but seeing the typed password is not ideal. In a real world example I wouldn’t be printing the password back either.

Secure keyboard input in bash

Turns out, read function supports this scenario – just update the script to this:

read -s PASS

-s is obviously short for secure.

Save the script and run it again, this time typing will not show, but later command should output our input just fine:

$ ./input.sh
Type your password, please:
You just typed: mypass

Pretty cool, huh?

See Also




Failed Login Attempts for root User

Failed login attempts

Wow, things are much worse than I expected!

I’m reinstalling one of the dedicated hosts for Tech Stack. Kicked off the reinstall with CentOS 8 and came back a few hours later, to see this:

[greys@s1 ~]$ su -
Password:
Last login: Tue Feb  4 01:09:22 CET 2020 on pts/1
Last failed login: Tue Feb  4 01:22:33 CET 2020 from 222.186.30.35 on ssh:notty
There were 84 failed login attempts since the last successful login.

This means that on average there’s 6 login attempts per minute… A good few hundred attempts each hour!

Definitely should switch to key-based SSH logins and deploy fail2ban as the priority!

See Also




shutdown vs halt

shutdown vs halt

Getting the difference between shutting a Unix system down versus halting it is kind of important.

Graceful shutdown

It’s important that your Unix/Linux system completes startup or shutdown in a graceful manner. What this means is that every process gets a chance to stop properly, rather than gets killed. It also means that these processes get stopped in a orderly manner – specifically following the order of appropriate startup/shutdown scripts or dependencies.

shutdown command does exactly that: it puts your desktop or server into a state of stopping services and preparing the server to be powered off.

Depending on your Unix/Linux implementation and command line options, a number of things will be requested by shutdown command before powering the system off:

  • warning about pending shutdown is broadcast to all the users logged into your system
  • a grace period (usually 1 minute) is started before shutdown proceeds
  • stop scripts are executed to correctly stop networked services
  • login attempts are blocked (new users won’t be able to log in)
  • processes are gracefully killed – meaning they can save data before shutting down

Relevant shutdown command options

If you want to immediately begin the shutdown procedure, you need to specify keyword now. If you want the server to power off and stay down, specify -h (for halt), if you want it to be rebooted, specify -r (for reboot).

IMPORTANT: If you don’t specify -h or -r, your Unix multiuser environment will be stopped and most OS services shut down, but the physical/virtual hardware will not be powered off. You’ll probably end up in a single user mode – where you can run admin commands as root.

Complete shutdown command for immediately bringing server down:

$ sudo shutdown -h now

Ungraceful shutdown: halt

halt command is another way to stop your Unix-like environment, but it’s more aggressive: no shutdown scripts or graceful process completion is allowed – it just stops Unix kernel.

halt also doesn’t really power your system off – it just stops your Unix/Linux environment from running. You still need to press the power button or activate the power switch.

See Also




Unix Tutorial Digest – February 2nd, 2020

Unix Tutorial Digest – January 2020

For those staying up late to watch Super Bowl LIV and those just looking for a few interesting links, here’s the monthly summary of Unix/Linux news and Unix Tutorial posts.

Please get in touch to arrange a technical consultation with me or suggest a useful link for the next digest here at Unix Tutorial.

Unix Tutorial News

Unix Tutorial website now has a chat feature! This means you can ping me and ask a quick question or discuss a project. My online (live chat) time is quite limited but please take a moment to leave me note and your email so that I can get back to you.

Unix Tutorial in Russian is now a completely separate website, I’ve been trying to keep up with translations and expect readership to grow significantly in the coming months.

Unix and Linux News

New Releases section has a few additions in January 2020:

Software News

Interesting and Useful

Unix Tutorial articles

Full list of posts published on Unix Tutorial in January 2020, 17 posts in total:

That’s it for the month of January 2020!

See Also




Show Files Installed by a Debian/Ubuntu Package

List files in a Debian/Ubuntu package with dpkg-query

Sometimes it’s not enough to know that a certain package is installed on your Linux system. You want to know the full list of files installed by the package, with exact locations of such files. This is when dpkg-query command may help.



Get List of Files Installed by a Package in Ubuntu

I mentioned xz-utils package for XZ archives yesterday, so let’s look at the xz-utils package. This is how I can get the full list of files installed by it:

greys@xps:~ $ dpkg-query -L xz-utils                                                                             [15/15]
 /.                                                                                                                      
 /usr                                                                                                                    
 /usr/bin                                                                                                                
 /usr/bin/lzmainfo                                                                                                       
 /usr/bin/xz                                                                                                             
 /usr/bin/xzdiff                                                                                                         
 /usr/bin/xzgrep                                                                                                         
 /usr/bin/xzless                                                                                                         
 /usr/bin/xzmore                                                                                                         
 /usr/share                                                                                                              
 /usr/share/doc                                                                                                          
 /usr/share/doc/xz-utils                                                                                                 
 /usr/share/doc/xz-utils/README.Debian                                                                                   
 /usr/share/doc/xz-utils/README.gz                                                                                       
 /usr/share/doc/xz-utils/copyright                                                                                       
 /usr/share/doc/xz-utils/extra
 /usr/share/doc/xz-utils/extra/7z2lzma
 /usr/share/doc/xz-utils/extra/7z2lzma/7z2lzma.bash
 /usr/share/doc/xz-utils/extra/scanlzma
 /usr/share/doc/xz-utils/extra/scanlzma/scanlzma.c
 /usr/share/doc/xz-utils/faq.txt.gz
 /usr/share/doc/xz-utils/history.txt.gz
 /usr/share/man
 /usr/share/man/man1
 /usr/share/man/man1/lzmainfo.1.gz
 /usr/share/man/man1/xz.1.gz
 /usr/share/man/man1/xzdiff.1.gz
 /usr/share/man/man1/xzgrep.1.gz
 /usr/share/man/man1/xzless.1.gz
 /usr/share/man/man1/xzmore.1.gz
 /usr/bin/unxz
 /usr/bin/xzcat
 /usr/bin/xzcmp
 /usr/bin/xzegrep
 /usr/bin/xzfgrep
 /usr/share/doc/xz-utils/AUTHORS
 /usr/share/doc/xz-utils/NEWS.gz
 /usr/share/doc/xz-utils/THANKS 
 /usr/share/doc/xz-utils/changelog.Debian.gz
 /usr/share/man/man1/unxz.1.gz
 /usr/share/man/man1/xzcat.1.gz 
 /usr/share/man/man1/xzcmp.1.gz 
 /usr/share/man/man1/xzegrep.1.gz
 /usr/share/man/man1/xzfgrep.1.gz

Find Binaries Installed by a Package

Simple grep will make the previous example even more useful. Let’s say we just want to know if a package installs any binaries, here’s how we can do it:

greys@xps:~ $ dpkg-query -L xz-utils | grep bin
 /usr/bin
 /usr/bin/lzmainfo
 /usr/bin/xz
 /usr/bin/xzdiff
 /usr/bin/xzgrep
 /usr/bin/xzless
 /usr/bin/xzmore
 /usr/bin/unxz
 /usr/bin/xzcat
 /usr/bin/xzcmp
 /usr/bin/xzegrep
 /usr/bin/xzfgrep

That’s it for today. Have fun!

See Also