内容简介:Last updated: 05/28/2020, byThis page lists the changes I make to a vanilla install of Arch Linux for security hardening, as well as some other changes I find useful. While Arch is my target platform, most of the changes will work on any Linux system that'
Last updated: 05/28/2020, by @blakkheim
This page lists the changes I make to a vanilla install of Arch Linux for security hardening, as well as some other changes I find useful. While Arch is my target platform, most of the changes will work on any Linux system that's reasonably up to date.
I typically favor security over performance. You may also see suggestions merely to make something more useful or shave precious seconds off a wait time. It's not a one-size-fits-all setup, but hopefully certain pieces of it will be useful.
I chose Arch for a few reasons:
- The install size: The base install is relatively minimal compared to a "prebuilt" distro like Fedora or Mint. This lets me focus on adding just what I want, rather than constantly trying to strip out things I don't need.
- The kernel: A common misconception about the Linux kernel is that it's secure, or that one can go a long time without worrying about kernel security updates. Neither of these are even remotely true. New versions of Linux are released almost every week, often containing security fixes buried among the many other changes. These releases typically don't make explicit mention of the changes having security implications. As a result, many "stable" or "LTS" distributions don't know which commits should be backported to their old kernels, or even that something needs backporting at all. If the problem has a public CVE assigned to it, maybe your distro will pick it up. Maybe not. Even if a CVE exists, at least in the case of Ubuntu and Debian especially, users are often left with kernels full of known holes for months at a time. Arch doesn't play the backporting game, instead opting to provide the newest stable releases shortly after they come out.
- The Arch Build System : Having enjoyed the ports system ofFreeBSD and OpenBSD for a long time, the ABS has been a pleasure to use. It makes building/rebuilding packages easy. It makes updating packages easy. It shows how things are actually built and with what options. This BSD-borrowed concept makes interacting with the package system simple and intuitive.
Now on to how I set things up.
Security Hardening
Other TweaksDisk Layout
This section contains a few tips to consider during your initial disk layout creation. The concepts should apply to any distrbution.
To start, consider using full disk encryption along with a Logical Volume Manager setup. Disk encryption protects data at rest, while LVM allows for some flexibility that can be quite useful. A simple disk layout might look like this:
-
/dev/sda1
(a small, unencrypted EFI System Partition , FAT32) mounted at/efi
(assuming this is a PC with UEFI, otherwise not needed) -
/dev/sda2
(a small, unencrypted ext4 partition) mounted at/boot
. -
/dev/sda3
(using the rest of the drive space) as the encrypted LUKS container for LVM
/
, /var
, and /home
in the install. For a typical
desktop, you probably want to give /home
most of the disk space.
The other two don't need much unless there's a specific use case in mind.
25GB and 8GB are used in this example. If you need to have a huge database
in /var
or something, make adjustments accordingly.
There are a lot of user-writable directories in Linux, each one providing
an opportunity for attackers to execute their own binaries.
Once the fstab
file is created, add the noexec
and nodev
flags
to /var
and /home
. Doing so will disallow execution
of binaries on these mount points, as well as prevent interpreting character
or block special devices on them. Two temporary filesystems ( /tmp
and /dev/shm
) can also be locked down with the same flags by
adding the following:
# /etc/fstab [...] tmpfs /tmp tmpfs rw,noexec,nodev,size=1G,mode=1777 0 0 tmpfs /dev/shm tmpfs rw,noexec,nodev,size=1G 0 0Adjust the
1G
size limit value as desired.
Once booted into the finished installation, it should look something like this:
# <strong>lvs</strong> LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert home lvm -wi-ao---- 189.12g root lvm -wi-ao---- 25.00g var lvm -wi-ao---- 8.00g # <strong>mount | egrep '(lvm|/tmp|shm)' | sort</strong> /dev/mapper/lvm-home on /home type ext4 (rw,nodev,noexec,relatime) /dev/mapper/lvm-root on / type ext4 (rw,relatime) /dev/mapper/lvm-var on /var type ext4 (rw,nodev,noexec,relatime) tmpfs on /dev/shm type tmpfs (rw,nodev,noexec,size=1048576k) tmpfs on /tmp type tmpfs (rw,nodev,noexec,relatime,size=1048576k)Another user-writable directory to consider is
/run
, specifically
the /run/user/$UID
directories that systemd spawns when someone
logs in, but their transience is annoying
and complicated
. I have yet to find the perfect solution there that won't break other things.
Pacman
Package managers usually don't need much additional configuration. Pacman , the one Arch uses, is no different. My recommendation for any package manager is simply to make sure that only HTTPS mirrorsare used.
# /etc/pacman.d/mirrorlist Server = <strong>https</strong>://example.com/[...]/$repo/os/$archCheck the mirrorlist generator
to see a list of TLS-capable servers near you.
Using an HTTPS mirror with Pacman is especially important because it doesn't validate the package database files and it runs everything as root . HTTPS doesn't mitigate either of these problems, but it is one line of defense against a MITM attack. I hope the developers will make fixing these two security issues a priority for the project soon. Other package managers have been doing it the right way for a long time.
Kernel Options
The linux-hardenedkernel package in Arch includes some compile-time security improvements that can't be set at runtime. Given the choice, I would always use it.
If your distribution doesn't have a package for it, applying the patchset to upstream sources and building your own kernel is pretty easy. If you go that route, have a look at the kconfig-hardened-check script for more compile-time settings to consider.
Runtime configuration of the kernel can be done in a number of ways. Desired flags may be passed on startup in the form of kernel parameters , of which there is an extensive list . Parameters are usually passed by the bootloader , so configuration details vary depending whether the system uses GRUB , systemd-boot , or something else.
The following options, split up into categories, are worth considering for security improvements:
l1tf=full,force spec_store_bypass_disable=on spectre_v2=onThese are addtional mitigations for certain CPU security flaws. While the
mitigations=auto
option is used by default in upstream
Linux, some of the mitigations it enables have been "toned down" for
performance reasons.
Examples of this include the L1TF
and Microarchitectural Data Sampling
vulnerabilities, which can't be fully mitigated unless HyperThreading
is disabled.
The Speculative Store Bypass
vulnerability is only partially mitigated by default, with applications being
allowed to opt-in for protections via prctl or seccomp.
Finally, we enable all mitigations (including those against userspace) for Spectre V2
.
apparmor=1 lsm=lockdown,yama,apparmor lockdown=<font><strong>XXX</strong></font>These enable the AppArmor , Yama , and Lockdown features, with the lockdown mode left for the reader to choose. Valid options are
integrity
and confidentiality
,
both described briefly here
.
Replace XXX
with whichever you see fit, or omit this option entirely if the feature isn't wanted.
For what it's worth, running in confidentiality
mode on my
desktop hasn't caused any problems. Your mileage and use case may vary.
Lockdown will break suspend-to-disk and any out-of-tree kernel modules
like ZFS, as well as DKMS
modules.
init_on_alloc=1 init_on_free=1 page_alloc.shuffle=1 slab_nomerge vsyscall=noneThis group will instruct the kernel to fill newly allocated pages and heap objects with zeroes, fill freed pages and heap objects with zeroes, tell the page allocator to randomize its free lists, disable merging of slabs with similar size, and disable vsyscalls
due to their history of making exploits easier. All five options are all set by default when using the linux-hardened kernel.
slub_debug=FThis enables sanity checks in the SLUB allocator . Two other flags to consider for non-hardened kernels are
Z
(redzoning, to detect when a slab is overwritten past its real size) and P
(to enable poisoning on slab cache allocations).
The full list of kernel parameters to be used must be specified on a single line, separated by spaces, in the bootloader's config file. An example for GRUB might look like this:
# /etc/default/grub [...] GRUB_CMDLINE_LINUX_DEFAULT="apparmor=1 init_on_alloc=1 init_on_free=1 l1tf=full,force lockdown=confidentiality lsm=lockdown,yama,apparmor page_alloc.shuffle=1 slab_nomerge slub_debug=F spec_store_bypass_disable=on spectre_v2=on vsyscall=none" [...]
Depending on the bootloader in use, the file may need to be regenerated after any edits are made.
Changes to the kernel parameters won't take effect until after a reboot. To verify they were applied, run:
$ <strong>cat /proc/cmdline</strong>
Yet more runtime options of the kernel can be configured through the sysctl
utility.
The values specified by any .conf
files in the /etc/sysctl.d
directory will be loaded during the boot sequence.
Here are some sysctl settings to consider.
# /etc/sysctl.d/99-sysctl.conf # prevent the automatic loading of <a href="https://en.wikipedia.org/wiki/Line_discipline">line disciplines</a> # <a href="https://lore.kernel.org/patchwork/patch/1034150">https://lore.kernel.org/patchwork/patch/1034150</a> dev.tty.ldisc_autoload=0 # <a href="https://www.kernel.org/doc/Documentation/sysctl/fs.txt">additional protections</a> for fifos, hardlinks, regular files, and symlinks # <a href="https://patchwork.kernel.org/patch/10244781">https://patchwork.kernel.org/patch/10244781</a> # slightly tightened up from the <a href="https://github.com/systemd/systemd/commit/2732587540035227fe59e4b64b60127352611b35">systemd default</a> values of "1" for each fs.protected_fifos=2 fs.protected_hardlinks=1 fs.protected_regular=2 fs.protected_symlinks=1 # prevent unprivileged users from viewing the dmesg buffer # (linux-hardened default) kernel.dmesg_restrict=1 # disable the kexec system call (can be used to replace the running kernel) # <a href="https://lwn.net/Articles/580269">https://lwn.net/Articles/580269</a> # (linux-hardened default) kernel.kexec_load_disabled=1 # impose restrictions on exposing kernel pointers # <a href="https://lwn.net/Articles/420403">https://lwn.net/Articles/420403</a> # (linux-hardened default) kernel.kptr_restrict=2 # restrict use of the performance events system by unprivileged users # <a href="https://lwn.net/Articles/696216">https://lwn.net/Articles/696216</a> # (linux-hardened default) kernel.perf_event_paranoid=3 # disable the "<a href="https://en.wikipedia.org/wiki/Magic_SysRq_key">magic sysrq key</a>" functionality # <a href="https://security.stackexchange.com/questions/138658">https://security.stackexchange.com/questions/138658</a> # <a href="https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1861238">https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1861238</a> # uncomment if the use of this feature is not needed #kernel.sysrq=0 # harden the BPF JIT compiler and restrict unprivileged use of BPF # <a href="https://www.zerodayinitiative.com/advisories/ZDI-20-350">https://www.zerodayinitiative.com/advisories/ZDI-20-350</a> # <a href="https://lwn.net/Articles/660331">https://lwn.net/Articles/660331</a> # (linux-hardened default) net.core.bpf_jit_harden=2 kernel.unprivileged_bpf_disabled=1 # disable unprivileged user namespaces # <a href="https://lwn.net/Articles/673597">https://lwn.net/Articles/673597</a> # (linux-hardened default) kernel.unprivileged_userns_clone=0 # enable yama ptrace restrictions # <a href="https://www.kernel.org/doc/Documentation/security/Yama.txt">https://www.kernel.org/doc/Documentation/security/Yama.txt</a> # (linux-hardened default) # set to "3" if the use of ptrace is not needed kernel.yama.ptrace_scope=1 # reverse path filtering to prevent some ip spoofing attacks # (default in some distributions) net.ipv4.conf.all.rp_filter=1 net.ipv4.conf.default.rp_filter=1 # disable <a href="https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol#Redirect">icmp redirects</a> and <a href="https://tools.ietf.org/html/rfc1620">RFC1620</a> shared media redirects net.ipv4.conf.all.accept_redirects=0 net.ipv4.conf.all.secure_redirects=0 net.ipv4.conf.all.send_redirects=0 net.ipv4.conf.all.shared_media=0 net.ipv4.conf.default.accept_redirects=0 net.ipv4.conf.default.secure_redirects=0 net.ipv4.conf.default.send_redirects=0 net.ipv4.conf.default.shared_media=0 net.ipv6.conf.all.accept_redirects=0 net.ipv6.conf.default.accept_redirects=0 # disallow <a href="https://en.wikipedia.org/wiki/Source_routing">source-routed packets</a> net.ipv4.conf.all.accept_source_route=0 net.ipv4.conf.default.accept_source_route=0 net.ipv6.conf.all.accept_source_route=0 net.ipv6.conf.default.accept_source_route=0 # ignore pings sent to a broadcast address (common for <a href="https://en.wikipedia.org/wiki/Smurf_attack">smurf attacks</a>) net.ipv4.icmp_echo_ignore_broadcasts=1 # ignore bogus icmp error responses net.ipv4.icmp_ignore_bogus_error_responses=1 # protect against time-wait assassination hazards in tcp # <a href="https://tools.ietf.org/html/rfc1337">https://tools.ietf.org/html/rfc1337</a> net.ipv4.tcp_rfc1337=1 # selective tcp acks have resulted in remotely exploitable crashes # <a href="https://lwn.net/Articles/791409">https://lwn.net/Articles/791409</a> # uncomment to potentially guard against future attacks # (may introduce a performance hit in highly congested networks) #net.ipv4.tcp_sack=0 # disable tcp timestamps to avoid leaking some system information # <a href="https://www.whonix.org/wiki/Disable_TCP_and_ICMP_Timestamps">https://www.whonix.org/wiki/Disable_TCP_and_ICMP_Timestamps</a> net.ipv4.tcp_timestamps=0 # increase aslr effectiveness for mmap # <a href="https://lwn.net/Articles/667790">https://lwn.net/Articles/667790</a> # (linux-hardened default) vm.mmap_rnd_bits=32 vm.mmap_rnd_compat_bits=16 # ignore icmp echo requests # uncomment if this system doesn't need to respond to pings #net.ipv4.icmp_echo_ignore_all=1 # disable creation of ipv6 addresses on network interfaces # uncomment if ipv6 is not in use #net.ipv6.conf.all.disable_ipv6=1 #net.ipv6.conf.default.disable_ipv6=1 #net.ipv6.conf.lo.disable_ipv6=1The extensive kernel documentation
has even more information about each of these options.
To apply the new configuration to a running system, run:
# <strong>sysctl -p /etc/sysctl.d/99-sysctl.conf</strong>
Changes will also be picked up automatically on the next reboot.
Firewall
Most systems should have some kind of firewall in place. There are a number of choices for this task on Linux. The simplest frontend I've found is called UFW . It uses an OpenBSD PF-like syntax and only takes a minute to get going.
# <strong>pacman -S ufw</strong> # <strong>sed -i -e 's/^\([^#].*\)/# \1/g' /etc/ufw/sysctl.conf</strong> # <strong>ufw deny in</strong> # <strong>ufw allow out</strong> # <strong>systemctl enable ufw</strong> # <strong>ufw enable</strong>This would create a basic firewall ruleset that blocks incoming connections and allows outgoing ones. If that's what you want, you're done. (One annoying part about UFW is the
/etc/ufw/sysctl.conf
file that comes with it. This file will override certain values in the main sysctl configuration, so I comment out everything there.)
If you're never going to actually read the logs UFW creates, might as well turn that feature off.
# <strong>ufw logging off</strong>
Another option to consider is a stricter policy for outgoing traffic: one that only permits connections on ports you actually use, and only to hosts that you want to allow. Here's an example.
# <strong>ufw deny in</strong> # <strong>ufw allow out proto tcp to any port 22</strong> # <strong>ufw allow out proto tcp to any port 443</strong> # <strong>ufw allow out proto udp to 192.168.1.1 port 123</strong> # <strong>ufw allow out proto udp to 192.168.1.1 port 53</strong> # <strong>ufw reject out</strong>
This would allow outgoing SSH and HTTPS connections to any host, allow outgoing DNS and NTP to a local server, and block all other outgoing traffic. Logging might be more useful in that case to detect misbehaving programs.
Make sure the ordering of your ruleset is correct and exactly what you want. Despite being inspired by OpenBSD's PF syntax, UFW uses a "first match wins" system rather than how PF does it the other way around. In other words, if you block all connections in rule 1 and allow a specific connection in rule 2, it will still be blocked by the first one.
Sudo
Many people use sudo, but few use it safely. Some distributions configure it in a way that allows regular users to become root by simply typing their own password. My concern with any usage of sudo that involves typing a password to elevate privileges stems from the fact that X11 allows any application to capture keystrokes.
My recommended sudo setup allows a regular user to do some administrative tasks as root without typing a password. These tasks, in my case, include updating packages and rebooting. Once my machine is set up the way I like it, that's really all I ever need to do as root.
# /etc/sudoers Defaults env_reset Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" Defaults umask=0022 Defaults umask_override root ALL=(ALL:ALL) ALL Cmnd_Alias PACMAN = /usr/bin/pacman -Syu Cmnd_Alias REBOOT = /sbin/reboot "" Cmnd_Alias SHUTDOWN = /sbin/poweroff "" bh ALL=(root) NOPASSWD: PACMAN bh ALL=(root) NOPASSWD: REBOOT bh ALL=(root) NOPASSWD: SHUTDOWNSuch a setup would allow my user to run
pacman -Syu
as root (but not any other pacman commands) as well as allow me to reboot and shut down the computer.
For any other administrative tasks, I would log in as root on a virtual console (ctrl + alt + f2) and do them there. This is slightly inconvenient, but the danger of X11 keylogging is real. Don't believe me? Here's a tiny keylogger that can save the sudo password being typed for later exploitation.
Application Sandboxing
If a bug in a program allows the process to be compromised, an attacker can essentially do anything that program can do: connect to the internet, read files, write files, and so on. Sandboxingis a way to limit the potential damage a compromised process can do.
Firejail is the tool I like the most for this task. It's easy to set up, provides reasonable defaults, and can be further hardened with straightforward config files. To install it on Arch, issue the following:
# <strong>pacman -S firejail</strong> # <strong>systemctl enable --now apparmor</strong> # <strong>apparmor_parser -r /etc/apparmor.d/firejail-default</strong> # <strong>firecfg</strong> # <strong>echo <strong><font>XXX</font></strong> > /etc/firejail/firejail.users</strong>Replace XXX
with your regular, non-root username.
Once that's in place, firejail
will create symbolic links to
any installed applications for which it has a profile. These are placed in
the /usr/local/bin
directory, meaning that your PATH
environment variable should point there first. This can be achieved by
exporting the variable in the user's shell rc file ( ~/.bashrc
or similar) or in /etc/profile
for all users.
[...] export PATH=/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbinConsider running
firecfg
after you've got all the programs installed that you'll need. Otherwise, it should be run again after any new applications are installed in case Firejail has a profile for them.
Additional capability restrictions can be put in place via custom profiles
in the user's ~/.config/firejail
directory. If you wanted to
disallow the Clementine music player from overwriting tags in your music
collection, the following example might work.
# /home/user/.config/firejail/clementine.profile whitelist ${HOME}/music read-only ${HOME}/musiC include /etc/firejail/clementine.profileTo test if it's working, use the
--list
flag while the sandboxed application is running.
$ <strong>firejail --list</strong> 14609:bh::/usr/bin/firejail /sbin/clementine 17755:bh::/usr/bin/firejail /sbin/firefoxMore information and examples can be found in the buiding custom profiles
section of the documentation.
RFKill (Disable WiFi / Bluetooth)
If your hardware has wireless or bluetooth capabilities and you want to disable them in software, rfkill can do that. It's included with the util-linuxpackage.
$ <strong>rfkill list</strong> ID TYPE DEVICE SOFT HARD 0 bluetooth hci0 blocked unblocked 1 wlan phy0 blocked unblocked
Blocking either or both at startup can be done by enabling the relevant service:
# <strong>systemctl enable --now rfkill-block@all.service</strong>Replace
all
with bluetooth
or wlan
as desired.
mkinitcpio
Arch uses mkinitcpio to generate the initial ramdiskimage. This runs every time your kernel is updated, so I think it's worth the time to optimize it bit. The stock configuration file has this block towards the end:
# /etc/mkinitcpio.conf [...] # COMPRESSION # Use this to compress the initramfs image. By default, gzip compression # is used. Use 'cat' to create an uncompressed image. COMPRESSION="gzip" #COMPRESSION="bzip2" #COMPRESSION="lzma" #COMPRESSION="xz" #COMPRESSION="lzop" #COMPRESSION="lz4" # COMPRESSION_OPTIONS # Additional options for the compressor #COMPRESSION_OPTIONS=()As it says, the script will use gzip
compression if left untouched. The gzip algorithm is old, slow, single-threaded, and not even very good at making the file smaller. The process can be sped up by using one of the more modern choices, like so:
[...] COMPRESSION="xz" COMPRESSION_OPTIONS=(-0 -T 0)This would use the much better xz
algorithm, which can also use all available CPU threads for compression. Doing so makes the process of rebuilding the image much faster on even semi-modern computers. As a bonus, the resulting image is even a bit smaller. Honestly, the files in question here are so small (20-60MB) that compression isn't really needed. But at least now it's faster.
NTP (Network Time Protocol)
As the clocks on most computers have a tendency to drift over time, it's a good idea to run some kind of NTPclient.
There are a variety of options available. The base Arch install includes (and any systemd-based distribution will include) systemd-timesyncd , but I'm not a big fan of it. If the thought of installing another package for a task that can technically be done with what you already have sends a shiver down your spine, avert your eyes now and use that one. I recommend OpenNTPD instead. It's lightweight and has an excellent security track record.
# <strong>pacman -S openntpd</strong> # <strong>systemctl disable --now systemd-timesyncd</strong> # <strong>systemctl enable openntpd</strong>It will use the ntp.org pool
by default for time synchronization:
# /etc/ntpd.conf [...] servers pool.ntp.org
That will work "fine," but the ntp.org pool includes a lot of low quality servers. Some of them are run in virtual machines. Some of them don't work anymore and were never removed. The round-robin DNS might even give you a server that's 100ms or more away from your actual location. Taking that into consideration, I'd recommend using some known-good servers in the config file as well.
server time.apple.com server time.cloudflare.com server pool.ntp.orgIf having company names in the config file scares you for some reason, there are plenty of other options
. I only suggest the Apple and Cloudflare pools because they have high quality nodes throughout the world and aren't likely to disappear any time soon.
The servers
keyword instructs OpenNTPD to use multiple IPs from
the domain, while server
means it will only use the first one.
It would use one from each pool in this case.
PulseAudio
My only recommendation for PulseAudio (other than to avoid using it) is to enable two options in the config file:
# /home/user/.config/pulse/daemon.conf avoid-resampling = true flat-volumes = noDoing so will prevent the audio quality from being needlessly degraded. It also prevents some frustrating issues
with volume control.
Other options to consider can be found in the audiophile-linux repository.
Miscellaneous
This section is for random tidbits that didn't really fit into the other parts.
Umask / Directory Permissions
I run my user with umask 77 by default, meaning that any newly created files will be unreadable by other users. My home directory is also mode 700. If a process running as another non-root user is compromised, it would be unable to read my files. To accomplish this, putumask 77
somewhere in the user's shell
rc file ( ~/.bashrc
or similar) and run:
# <strong>chmod -R go-rwx /home/*</strong>
Never change root's umask value. Doing so will cause all sorts of problems.
More tmpfs
I also like putting my user's~/.cache
directory in tmpfs to reduce disk writes.
# /etc/fstab [...] tmpfs /home/bh/.cache tmpfs rw,size=250M,noexec,noatime,nodev,uid=bh,gid=bh,mode=700 0 0
Everything that goes there is junk anyway.
DNSCrypt
Outgoing DNS lookups can be encrypted with dnscrypt-proxy. There are other options like DNS over HTTPS and DNS over TLS, but I like the DNSCrypt protocol more because it doesn't rely on the certificate authority model.
More sysctl
The following non-security-relatedsysctl
settings have been useful in my experience:
# /etc/sysctl.d/98-misc.conf net.ipv4.tcp_congestion_control=bbr vm.swappiness=10Linux supports multiple TCP congestion control algorithms . The BBR algorithm gives more consistent network throughput than the default
in my experience, especially for transatlantic file transfers. It may help a lot, or it may not make a difference at all, depending on the use case.
The " swappiness
"
value controls how aggressively the kernel will swap out memory pages to disk.
The default value of 60
is way too high for me, so I turn it down
to 10
to prevent so much swapping.
Journal Size
The systemd log can get pretty huge if you don't place any limiton it. Compression can also be enabled so that more information will fit in the smaller file.
# /etc/systemd/journald.conf.d/99-limit.conf [Journal] Compress=yes SystemMaxUse=100M
Adjust the size to whatever you think is reasonable.
Closing
Thanks for reading. Send me your comments, suggestions, or critiques: I'm at twitter.com/blakkheim.
If you found anything on this page useful, feel free to...
以上所述就是小编给大家介绍的《Linux Security Hardening and Other Tweaks》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
iOS编程实战
[美] Rob Napier、[美] Mugunth Kumar / 美团移动 / 人民邮电出版社 / 2014-9 / 79.00元
本书深入介绍iOS 7新特性和新功能,涵盖iOS 7大部分新增特性,包括新的后台操作、Core Bluetooth、UIKit动力学以及TextKit。另外还介绍了如何处理新的扁平化UI,并新增了一章你可能不知道的“小技巧”。如果读者熟练掌握C和C++,读完本书即可创建性能优异的iPhone、iPad和iPod touch应用。 本书主要内容包括: iOS 7新特性和新功能概览; ......一起来看看 《iOS编程实战》 这本书的介绍吧!