defensive-coding-guide/_preview/fedora/master/en-US/programming-languages/Shell.html

699 lines
32 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Defensive Coding Guide | Defensive Coding Guide | Programming Languages | Shell Programming and bash</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<!-- Overpass Font -->
<link rel="stylesheet" href="https://overpass-30e2.kxcdn.com/overpass.css">
<link href="../../../master/_stylesheets/asciibinder.css" rel="stylesheet" />
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<link href="../../../master/_images/favicon32x32.png" rel="shortcut icon" type="text/css">
<!--[if IE]><link rel="shortcut icon" href="../../../master/_images/favicon.ico"><![endif]-->
<meta content="AsciiBinder" name="application-name">
</head>
<body>
<div class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="https://docs.fedoraproject.org/"><img alt="Fedora Documentation" src="../../../master/_images/fedora.svg"></a>
</div>
</div>
</div>
<div class="container">
<p class="toggle-nav visible-xs pull-left">
<button class="btn btn-default btn-sm" type="button" data-toggle="offcanvas">Toggle nav</button>
</p>
<ol class="breadcrumb">
<li class="sitename">
<a href="../../../index.html">Home</a>
</li>
<li class="hidden-xs active">
<a href="../../en-US/index.html">Defensive Coding Guide </a>
</li>
<li class="hidden-xs active">
<a href="../../en-US/index.html">Defensive Coding Guide</a>
</li>
<li class="hidden-xs active"><a href="../../en-US/programming-languages/C.html">Programming Languages</a></li>
<li class="hidden-xs active">
Shell Programming and bash
</li>
</ol>
<div class="row row-offcanvas row-offcanvas-left">
<div class="col-xs-8 col-sm-3 col-md-3 sidebar sidebar-offcanvas">
<ul class="nav nav-sidebar">
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicGroup0">
<span id="tgSpan0" class="fa fa-angle-down"></span>Defensive Coding Guide
</a>
<ul id="topicGroup0" class="collapse in list-unstyled">
<li><a class="" href="../../en-US/index.html">Book Information</a></li>
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicSubGroup-0-1">
<span id="sgSpan-0-1" class="fa fa-caret-down"></span>&nbsp;Programming Languages
</a>
<ul id="topicSubGroup-0-1" class="nav-tertiary list-unstyled collapse in">
<li><a class="" href="../../en-US/programming-languages/C.html">The C Programming Language</a></li>
<li><a class="" href="../../en-US/programming-languages/CXX.html">The C++ Programming&nbsp;Language</a></li>
<li><a class="" href="../../en-US/programming-languages/Java.html">The Java Programming Language</a></li>
<li><a class="" href="../../en-US/programming-languages/Python.html">The Python Programming Language</a></li>
<li><a class=" active" href="../../en-US/programming-languages/Shell.html">Shell Programming and bash</a></li>
<li><a class="" href="../../en-US/programming-languages/Go.html">The Go Programming Language</a></li>
<li><a class="" href="../../en-US/programming-languages/Vala.html">The Vala Programming Language</a></li>
</ul>
</li>
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicSubGroup-0-2">
<span id="sgSpan-0-2" class="fa fa-caret-right"></span>&nbsp;Specific Programming Tasks
</a>
<ul id="topicSubGroup-0-2" class="nav-tertiary list-unstyled collapse">
<li><a class="" href="../../en-US/tasks/Tasks-Library_Design.html">Library Design</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Descriptors.html">File Descriptor Management</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-File_System.html">File System Manipulation</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Temporary_Files.html">Temporary Files</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Processes.html">Processes</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Serialization.html">Serialization and Deserialization</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Cryptography.html">Cryptography</a></li>
<li><a class="" href="../../en-US/tasks/Tasks-Packaging.html">RPM Packaging</a></li>
</ul>
</li>
<li class="nav-header">
<a class="" href="#" data-toggle="collapse" data-target="#topicSubGroup-0-3">
<span id="sgSpan-0-3" class="fa fa-caret-right"></span>&nbsp;Implementing Security Features
</a>
<ul id="topicSubGroup-0-3" class="nav-tertiary list-unstyled collapse">
<li><a class="" href="../../en-US/features/Features-Authentication.html">Authentication and Authorization</a></li>
<li><a class="" href="../../en-US/features/Features-TLS.html">Transport Layer Security (TLS)</a></li>
<li><a class="" href="../../en-US/features/Features-HSM.html">Hardware Security Modules and Smart Cards</a></li>
</ul>
</li>
<li><a class="" href="../../en-US/Revision_History.html">Revision History</a></li>
</ul>
</li>
</ul>
</div>
<div class="col-xs-12 col-sm-9 col-md-9 main">
<div class="page-header">
<h2>Shell Programming and <strong class="application">bash</strong></h2>
</div>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This chapter contains advice about shell programming, specifically
in <strong class="application">bash</strong>. Most of the advice will apply
to scripts written for other shells because extensions such as
integer or array variables have been implemented there as well, with
comparable syntax.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-Shell-Alternatives"><a class="anchor" href="#sect-Defensive_Coding-Shell-Alternatives"></a>Consider Alternatives</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Once a shell script is so complex that advice in this chapter
applies, it is time to step back and consider the question: Is
there a more suitable implementation language available?</p>
</div>
<div class="paragraph">
<p>For example, Python with its <code>subprocess</code> module
can be used to write scripts which are almost as concise as shell
scripts when it comes to invoking external programs, and Python
offers richer data structures, with less arcane syntax and more
consistent behavior.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-Shell-Language"><a class="anchor" href="#sect-Defensive_Coding-Shell-Language"></a>Shell Language Features</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The following sections cover subtleties concerning the shell
programming languages. They have been written with the
<strong class="application">bash</strong> shell in mind, but some of these
features apply to other shells as well.</p>
</div>
<div class="paragraph">
<p>Some of the features described may seem like implementation defects,
but these features have been replicated across multiple independent
implementations, so they now have to be considered part of the shell
programming language.</p>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-Shell-Parameter_Expansion"><a class="anchor" href="#sect-Defensive_Coding-Shell-Parameter_Expansion"></a>Parameter Expansion</h3>
<div class="paragraph">
<p>The mechanism by which named shell variables and parameters are
expanded is called <strong>parameter expansion</strong>. The
most basic syntax is
<code>$</code><strong>variable</strong>” or
<code>${</code><strong>variable</strong><code>}</code>”.</p>
</div>
<div class="paragraph">
<p>In almost all cases, a parameter expansion should be enclosed in
double quotation marks <code>&#8220;&#8221;</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">external-program &quot;$arg1&quot; &quot;$arg2&quot;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If the double quotation marks are omitted, the value of the
variable will be split according to the current value of the
<code>IFS</code> variable. This may allow the injection of
additional options which are then processed by
<code>external-program</code>.</p>
</div>
<div class="paragraph">
<p>Parameter expansion can use special syntax for specific features,
such as substituting defaults or performing string or array
operations. These constructs should not be used because they can
trigger arithmetic evaluation, which can result in code execution.
See <a href="#sect-Defensive_Coding-Shell-Arithmetic">Arithmetic Evaluation</a>.</p>
</div>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-Shell-Double_Expansion"><a class="anchor" href="#sect-Defensive_Coding-Shell-Double_Expansion"></a>Double Expansion</h3>
<div class="paragraph">
<p><strong>Double expansion</strong> occurs when, during the
expansion of a shell variable, not just the variable is expanded,
replacing it by its value, but the <strong>value</strong> of
the variable is itself is expanded as well. This can trigger
arbitrary code execution, unless the value of the variable is
verified against a restrictive pattern.</p>
</div>
<div class="paragraph">
<p>The evaluation process is in fact recursive, so a self-referential
expression can cause an out-of-memory condition and a shell crash.</p>
</div>
<div class="paragraph">
<p>Double expansion may seem like as a defect, but it is implemented
by many shells, and has to be considered an integral part of the
shell programming language. However, it does make writing robust
shell scripts difficult.</p>
</div>
<div class="paragraph">
<p>Double expansion can be requested explicitly with the
<code>eval</code> built-in command, or by invoking a
subshell with “<code>bash -c</code>”. These constructs
should not be used.</p>
</div>
<div class="paragraph">
<p>The following sections give examples of places where implicit
double expansion occurs.</p>
</div>
<div class="sect3">
<h4 id="sect-Defensive_Coding-Shell-Arithmetic"><a class="anchor" href="#sect-Defensive_Coding-Shell-Arithmetic"></a>Arithmetic Evaluation</h4>
<div class="paragraph">
<p><strong>Arithmetic evaluation</strong> is a process by which
the shell computes the integer value of an expression specified
as a string. It is highly problematic for two reasons: It
triggers double expansion (see <a href="#sect-Defensive_Coding-Shell-Double_Expansion">Double Expansion</a>), and the
language of arithmetic expressions is not self-contained. Some
constructs in arithmetic expressions (notably array subscripts)
provide a trapdoor from the restricted language of arithmetic
expressions to the full shell language, thus paving the way
towards arbitrary code execution. Due to double expansion,
input which is (indirectly) referenced from an arithmetic
expression can trigger execution of arbitrary code, which is
potentially harmful.</p>
</div>
<div class="paragraph">
<p>Arithmetic evaluation is triggered by the follow constructs:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The <strong>expression</strong> in
<code>$</code><strong>expression</strong><code></code>
is evaluated. This construct is called <strong>arithmetic
expansion</strong>.</p>
</li>
<li>
<p></p>
<div class="paragraph">
<p><code>$[</code><strong>expression</strong><code>]</code>
is a deprecated syntax with the same effect.</p>
</div>
</li>
<li>
<p>The arguments to the <code>let</code> shell built-in
are evaluated.</p>
</li>
<li>
<p></p>
<div class="paragraph">
<p><code></code><strong>expression</strong><code></code>
is an alternative syntax for “<code>let</code> <strong>expression</strong>”.</p>
</div>
</li>
<li>
<p>Conditional expressions surrounded by
<code>[[</code><code>]]</code>” can trigger
arithmetic evaluation if certain operators such as
<code>-eq</code> are used. (The
<code>test</code> built-in does not perform arithmetic
evaluation, even with integer operators such as
<code>-eq</code>.)</p>
<div class="paragraph">
<p>The conditional expression
<code>[[ $</code><strong>variable</strong> <code>=~</code> <strong>regexp</strong> <code>]]</code>
can be used for input validation, assuming that
<strong>regexp</strong> is a constant regular
expression.
See <a href="#sect-Defensive_Coding-Shell-Input_Validation">Performing Input Validation</a>.</p>
</div>
</li>
<li>
<p>Certain parameter expansions, for example
<code>${</code><strong>variable</strong><code>[</code><strong>expression</strong><code>]}</code>
(array indexing) or
<code>${</code><strong>variable</strong><code>:</code><strong>expression</strong><code>}</code>
(string slicing), trigger arithmetic evaluation of
<strong>expression</strong>.</p>
</li>
<li>
<p>Assignment to array elements using
<strong>array_variable</strong><code>[</code><strong>subscript</strong><code>]=</code><strong>expression</strong>
triggers evaluation of <strong>subscript</strong>, but
not <strong>expression</strong>.</p>
</li>
<li>
<p>The expressions in the arithmetic <code>for</code>
command,
<code>for </code><strong>expression1</strong><code>;</code> <strong>expression2</strong><code>;</code> <strong>expression3</strong><code>; do</code> <strong>commands</strong><code>; done</code>
are evaluated. This does not apply to the regular
for command,
<code>for</code> <strong>variable</strong> <code>in</code> <strong>list</strong><code>; do</code> <strong>commands</strong><code>; done</code>”.</p>
</li>
</ul>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Depending on the <strong class="application">bash</strong> version, the
above list may be incomplete.</p>
</div>
<div class="paragraph">
<p>If faced with a situation where using such shell features
appears necessary, see <a href="#sect-Defensive_Coding-Shell-Alternatives">Consider Alternatives</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If it is impossible to avoid shell arithmetic on untrusted
inputs, refer to <a href="#sect-Defensive_Coding-Shell-Input_Validation">Performing Input Validation</a>.</p>
</div>
</div>
<div class="sect3">
<h4 id="sect-Defensive_Coding-Shell-Types"><a class="anchor" href="#sect-Defensive_Coding-Shell-Types"></a>Type declarations</h4>
<div class="paragraph">
<p><strong class="application">bash</strong> supports explicit type
declarations for shell variables:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash"> declare -i integer_variable
declare -a array_variable
declare -A assoc_array_variable
typeset -i integer_variable
typeset -a array_variable
typeset -A assoc_array_variable
local -i integer_variable
local -a array_variable
local -A assoc_array_variable
readonly -i integer_variable
readonly -a array_variable
readonly -A assoc_array_variable</code></pre>
</div>
</div>
<div class="paragraph">
<p>Variables can also be declared as arrays by assigning them an
array expression, as in:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">array_variable=(1 2 3 4)</code></pre>
</div>
</div>
<div class="paragraph">
<p>Some built-ins (such as <code>mapfile</code>) can
implicitly create array variables.</p>
</div>
<div class="paragraph">
<p>Such type declarations should not be used because assignment to
such variables (independent of the concrete syntax used for the
assignment) triggers arithmetic expansion (and thus double
expansion) of the right-hand side of the assignment operation.
See <a href="#sect-Defensive_Coding-Shell-Arithmetic">Arithmetic Evaluation</a>.</p>
</div>
<div class="paragraph">
<p>Shell scripts which use integer or array variables should be
rewritten in another, more suitable language. Se <a href="#sect-Defensive_Coding-Shell-Alternatives">Consider Alternatives</a>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="sect-Defensive_Coding-Shell-Obscure"><a class="anchor" href="#sect-Defensive_Coding-Shell-Obscure"></a>Other Obscurities</h3>
<div class="paragraph">
<p>Obscure shell language features should not be used. Examples are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Exported functions (<code>export -f</code> or
<code>declare -f</code>).</p>
</li>
<li>
<p>Function names which are not valid variable names, such as
<code>module::function</code>”.</p>
</li>
<li>
<p>The possibility to override built-ins or external commands
with shell functions.</p>
</li>
<li>
<p>Changing the value of the <code>IFS</code> variable to
tokenize strings.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-Shell-Invoke"><a class="anchor" href="#sect-Defensive_Coding-Shell-Invoke"></a>Invoking External Commands</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When passing shell variables as single command line arguments,
they should always be surrounded by double quotes. See
<a href="#sect-Defensive_Coding-Shell-Parameter_Expansion">Parameter Expansion</a>.</p>
</div>
<div class="paragraph">
<p>Care is required when passing untrusted values as positional
parameters to external commands. If the value starts with a hyphen
<code>-</code>”, it may be interpreted by the external
command as an option. Depending on the external program, a
<code>--</code>” argument stops option processing and treats
all following arguments as positional parameters. (Double quotes
are completely invisible to the command being invoked, so they do
not prevent variable values from being interpreted as options.)</p>
</div>
<div class="paragraph">
<p>Cleaning the environment before invoking child processes is
difficult to implement in script. <strong class="application">bash</strong>
keeps a hidden list of environment variables which do not correspond
to shell variables, and unsetting them from within a
<strong class="application">bash</strong> script is not possible. To reset
the environment, a script can re-run itself under the “<code>env
-i</code>” command with an additional parameter which indicates
the environment has been cleared and suppresses a further
self-execution. Alternatively, individual commands can be executed
with “<code>env -i</code>”.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Complete isolation from its original execution environment
(which is required when the script is executed after a trust
transition, e.g., triggered by the SUID mechanism) is impossible
to achieve from within the shell script itself. Instead, the
invoking process has to clear the process environment (except for
few trusted variables) before running the shell script.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Checking for failures in executed external commands is recommended.
If no elaborate error recovery is needed, invoking “<code>set
-e</code>” may be sufficient. This causes the script to stop on
the first failed command. However, failures in pipes
(“<code>command1 | command2</code>”) are only detected for the
last command in the pipe, errors in previous commands are ignored.
This can be changed by invoking “<code>set -o pipefail</code>”.
Due to architectural limitations, only the process that spawned
the entire pipe can check for failures in individual commands;
it is not possible for a process to tell if the process feeding
data (or the process consuming data) exited normally or with
an error.</p>
</div>
<div class="paragraph">
<p>See <a href="#sect-Defensive_Coding-Tasks-Processes-Creation">[sect-Defensive_Coding-Tasks-Processes-Creation]</a>
for additional details on creating child processes.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-Shell-Temporary_Files"><a class="anchor" href="#sect-Defensive_Coding-Shell-Temporary_Files"></a>Temporary Files</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Temporary files should be created with the
<code>mktemp</code> command, and temporary directories with
<code>mktemp -d</code>”.</p>
</div>
<div class="paragraph">
<p>To clean up temporary files and directories, write a clean-up
shell function and register it as a trap handler, as shown in
<a href="#ex-Defensive_Coding-Tasks-Temporary_Files">Creating and Cleaning up Temporary Files</a>.
Using a separate function avoids issues with proper quoting of
variables.</p>
</div>
<div id="ex-Defensive_Coding-Tasks-Temporary_Files" class="exampleblock">
<div class="title">Example 1. Creating and Cleaning up Temporary Files</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">tmpfile=&quot;$(mktemp)&quot;
cleanup () {
rm -f -- &quot;$tmpfile&quot;
}
trap cleanup 0</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-Shell-Input_Validation"><a class="anchor" href="#sect-Defensive_Coding-Shell-Input_Validation"></a>Performing Input Validation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In some cases, input validation cannot be avoided. For example,
if arithmetic evaluation is absolutely required, it is imperative
to check that input values are, in fact, integers. See <a href="#sect-Defensive_Coding-Shell-Arithmetic">Arithmetic Evaluation</a>.</p>
</div>
<div class="paragraph">
<p><a href="#ex-Defensive_Coding-Shell-Input_Validation">Input validation in <strong class="application">bash</strong></a>
shows a construct which can be used to check if a string
<code>$value</code>” is an integer. This construct is
specific to <strong class="application">bash</strong> and not portable to
POSIX shells.</p>
</div>
<div id="ex-Defensive_Coding-Shell-Input_Validation" class="exampleblock">
<div class="title">Example 2. Input validation in <strong class="application">bash</strong></div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">Unresolved directive in &lt;stdin&gt; - include::../snippets/Shell-Input_Validation.adoc[]</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Using <code>case</code> statements for input validation is
also possible and supported by other (POSIX) shells, but the
pattern language is more restrictive, and it can be difficult to
write suitable patterns.</p>
</div>
<div class="paragraph">
<p>The <code>expr</code> external command can give misleading
results (e.g., if the value being checked contains operators
itself) and should not be used.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="sect-Defensive_Coding-Shell-Edit_Guard"><a class="anchor" href="#sect-Defensive_Coding-Shell-Edit_Guard"></a>Guarding Shell Scripts Against Changes</h2>
<div class="sectionbody">
<div class="paragraph">
<p><strong class="application">bash</strong> only reads a shell script up to
the point it is needed for executed the next command. This means
that if script is overwritten while it is running, execution can
jump to a random part of the script, depending on what is modified
in the script and how the file offsets change as a result. (This
behavior is needed to support self-extracting shell archives whose
script part is followed by a stream of bytes which does not follow
the shell language syntax.)</p>
</div>
<div class="paragraph">
<p>Therefore, long-running scripts should be guarded against
concurrent modification by putting as much of the program logic
into a <code>main</code> function, and invoking the
<code>main</code> function at the end of the script, using
this syntax:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="CodeRay highlight"><code data-lang="bash">main &quot;$@&quot; ; exit $?</code></pre>
</div>
</div>
<div class="paragraph">
<p>This construct ensures that <strong class="application">bash</strong> will
stop execution after the <code>main</code> function, instead
of opening the script file and trying to read more commands.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="bottom" class="text-muted py-3" >
<div class="foot">
<div class="container">
<div class="row footerlinks">
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title">About</h3>
<div class="widget-body">
<dl>
<dd><a href="https://fedoraproject.org/wiki/Overview">About Fedora</a></dd>
<dd><a href="https://getfedora.org/en/sponsors">Sponsors</a></dd>
<dd><a href="https://fedoramagazine.org">Fedora Magazine</a></dd>
<dd><a href="https://fedoraproject.org/wiki/Legal:Main#Legal">Legal</a></dd>
</dl>
<ul class="list-inline">
<li>
<a href="https://www.facebook.com/TheFedoraProject" class="btn-social btn-outline"><i class="fa fa-fw fa-facebook"></i></a>
</li>
<li>
<a href="https://plus.google.com/112917221531140868607" class="btn-social btn-outline"><i class="fa fa-fw fa-google-plus"></i></a>
</li>
<li>
<a href="https://twitter.com/fedora" class="btn-social btn-outline"><i class="fa fa-fw fa-twitter"></i></a>
</li>
</ul>
</div>
</div>
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title uppercase">Download</h3>
<div class="widget-body">
<dl>
<dd><a href="https://getfedora.org/en/workstation/download">Get Fedora Workstation</a></dd>
<dd><a href="https://getfedora.org/en/server/download">Get Fedora Server</a></dd>
<dd><a href="https://getfedora.org/en/atomic/download">Get Fedora Atomic</a></dd>
<dd><a href="https://spins.fedoraproject.org">Fedora Spins</a></dd>
<dd><a href="https://labs.fedoraproject.org">Fedora Labs</a></dd>
<dd><a href="https://arm.fedoraproject.org">Fedora ARM<span class="sup">&reg;</span></a></dd>
<dd><a href="https://alt.fedoraproject.org/">Alternative Downloads</a></dd>
</dl>
</div>
</div>
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title">Support</h3>
<div class="widget-body">
<dl>
<dd><a href="https://fedoraproject.org/wiki/Communicating_and_getting_help">Get Help</a></dd>
<dd><a href="https://ask.fedoraproject.org/">Ask Fedora</a></dd>
<dd><a href="https://fedoraproject.org/wiki/Common_F27_bugs">Common Bugs</a></dd>
<dd><a href="https://developer.fedoraproject.org/">Fedora Developer Portal</a></dd>
<dd><a href="https://docs.fedoraproject.org/f27/install-guide/index.html">Installation Guide</a></dd>
</dl>
</div>
</div>
<div class="col-sm-3 col-xs-6 widget">
<h3 class="widget-title">Join</h3>
<div class="widget-body">
<dl>
<dd><a href="https://fedoraproject.org/wiki/Join">Join Fedora</a></dd>
<dd><a href="http://fedoraplanet.org">Planet Fedora</a></dd>
<dd><a href="https://fedoraproject.org/wiki/SIGs">Fedora SIGs</a></dd>
<dd><a href="https://admin.fedoraproject.org/accounts/">Fedora Account System</a></dd>
<dd><a href="https://fedoracommunity.org/">Fedora Community</a></dd>
</dl>
</div>
</div>
</div> <!-- /row of widgets -->
<div class="row">
<div class="col-md-2">
<div class="widget-body">
<a href="https://www.redhat.com/"><img class="rh-logo" src="../../../master/_images/redhat-logo.png" alt="Red Hat Logo" /></a>
</div>
</div>
<div class="col-md-7">
<div class="widget-body">
<p class="sponsor">Fedora is sponsored by Red Hat.</p>
<p class="sponsor"><a href="https://www.redhat.com/en/technologies/linux-platforms/articles/relationship-between-fedora-and-rhel">Learn more about the relationship between Red Hat and Fedora &raquo;</a></p>
<p class="copy">&copy; 2017 Red Hat, Inc. and others. Please send any comments or corrections to the <a href="https://pagure.io/fedora-docs/docs-fp-o">documentation team</a></p>
</div>
</div>
</div> <!-- /row of widgets -->
</div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="../../../master/_javascripts/bootstrap-offcanvas.js" type="text/javascript"></script>
<script type="text/javascript">
/*<![CDATA[*/
$(document).ready(function() {
$("[id^='topicGroup']").on('show.bs.collapse', function (event) {
if (!($(event.target).attr('id').match(/^topicSubGroup/))) {
$(this).parent().find("[id^='tgSpan']").toggleClass("fa-angle-right fa-angle-down");
}
});
$("[id^='topicGroup']").on('hide.bs.collapse', function (event) {
if (!($(event.target).attr('id').match(/^topicSubGroup/))) {
$(this).parent().find("[id^='tgSpan']").toggleClass("fa-angle-right fa-angle-down");
}
});
$("[id^='topicSubGroup']").on('show.bs.collapse', function () {
$(this).parent().find("[id^='sgSpan']").toggleClass("fa-caret-right fa-caret-down");
});
$("[id^='topicSubGroup']").on('hide.bs.collapse', function () {
$(this).parent().find("[id^='sgSpan']").toggleClass("fa-caret-right fa-caret-down");
});
});
/*]]>*/
</script>
</body>
</html>