Showing posts with label systemd. Show all posts
Showing posts with label systemd. Show all posts

Saturday, May 18, 2024

Configuring an interface in Linux without(!) nmcli

In ages past, when you wanted to configure a network interface eth0 with MAC CC:00:FF:EE:12:34 with static IP 128.227.3.20 and using uranus (128.227.3.1) as the gateway in Red Hat Linux or its derivatives, you could have something like this

cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << 'EOF'
DEVICE="eth0"
BOOTPROTO="static"
HWADDR="CC:00:FF:EE:12:34"
NM_CONTROLLED="no"
ONBOOT="yes"
TYPE="Ethernet"
DHCP_HOSTNAME=vmhost
IPADDR=128.227.3.20
NETMASK=255.255.255.0
GATEWAY=128.227.3.1
EOF

in your notes and cut-n-paste it as needed. The same would work for a vlan trunk (think 802.1q), where the interface using 128.227.3.20 is now associated to tagged vlan 3; all we needed is 3 files.

  • Define the base interface (we could have used uuid instead of MAC address)
    cat /etc/sysconfig/network-scripts/ifcfg-eno1 << 'EOF'
    TYPE=Ethernet
    NAME="eno1"
    DEVICE="eno1"
    HWADDR="CC:00:FF:EE:12:34"
    BOOTPROTO="none"
    ONBOOT=yes
  • Define the tagged vlan 3, and which bridge it is associated with
    cat /etc/sysconfig/network-scripts/ifcfg-eno1.3 << 'EOF'
    DEVICE="eno1.3"
    BOOTPROTO="none"
    NM_CONTROLLED="no"
    ONBOOT="yes"
    VLAN=yes
    BRIDGE=dmzbr
    EOF
  • Define the bridge with static IP
    cat /etc/sysconfig/network-scripts/ifcfg-dmzbr << 'EOF'
    DEVICE=dmzbr
    TYPE=Bridge
    BOOTPROTO="static"
    NM_CONTROLLED="no"
    ONBOOT="yes"
    TYPE="Ethernet"
    DHCP_HOSTNAME=vmhost
    IPADDR=128.227.3.20
    NETMASK=255.255.255.0
    GATEWAY=128.227.3.1
    EOF

We could get away with two but the 3rd one is there because I like to use bridges. The awake reader will have noticed I switched from eth0 to eno1; I will leave that to the reader but the point here is the above applies to whatever networking naming convention you have to deal with.

You shalll count to three (Monty Python)

As some (those who do not fall asleep reading these posts) know, I like automation and one of my tools of choice is ansible. Creating the above files for a given host from network declarations in its host_vars/host is very convenient using ansible.

But, there is Network Manager. Until recently -- CentOS 8, Rocky 8, Alma 8 -- you could still tell Network Manager to leave these interfaces alone (the NM_CONTROLLED entry), but now I am building a rocky 9 host, I am being forced to use nmcli instead. And what would it take to nmcli all of that? Let's find out (I think I missed a step here, so don't trust this):

nmcli con add ifname dmzbr type bridge con-name dmzbr
nmcli con modify dmzbr ipv6.method disabled
nmcli connection modify dmzbr ipv4.address 192.168.3.20/24
nmcli connection modify dmzbr ipv4.gateway 192.168.3.1
nmcli connection modify dmzbr ipv4.dns 192.168.3.1
nmcli connection add type vlan con-name eno1.3 ifname eno1.3 dev eno1 id 3
nmcli connection modify eno1.3 master dmzbr slave-type bridge
nmcli connection up eno1.3
nmcli connection up dmzbr

And this should end up with something like this

[root@testbox ~]# nmcli con show
NAME       UUID                                  TYPE      DEVICE
DMZ        6a97eddf-ac72-45d9-ba29-d12c7d59b511  vlan      eno1.3
dmzBridge  ccffabb8-0c8a-47d8-a2d6-15cab0e9b53b  bridge    dmzbr
eno1       9b1b155f-8197-3a7a-a9cc-79cab8b92da1  ethernet  eno1
lo         2dd22561-0fd3-436a-9da0-a53c61d63848  loopback  lo
[root@testbox ~]#

Before you get excited, that UUID is not set in stone. If you are going to do this in Ansible, take a look at the official docs on the nmcli_module. Short version is you are doing all those nmcli commands I did before, which I know I need to check since I know I missed something (I changed the bridge name later). And that is the proper official way to do the deed.

And then there is you

Yep, there is me. I know I will make a mistake. So, let's take a look on this network manager thing. No matter what, the configuration of all of these network interfaces have to go somewhere? If I am unlucky, some kind of binary-only database like Microsoft. If I am lucky, a text file. Well, it turned out my luck still holds: the files are hidden in /etc/NetworkManager/system-connections/:

[root@testbox ~]# ls /etc/NetworkManager/system-connections/
dmzbr.nmconnection  eno1.nmconnection   DMZ.nmconnection
[root@testbox ~]# 

Let's take a look at eno1:

[root@testbox ~]# cat /etc/NetworkManager/system-connections/eno1.nmconnection 
[connection]
id=eno1
uuid=9b1b155f-8197-3a7a-a9cc-79cab8b92da1
type=ethernet
interface-name=eno1
timestamp=1711852227

[ethernet]

[ipv4]
method=disabled

[ipv6]
addr-gen-mode=eui64
method=disabled

[proxy]
[root@testbox ~]# 

admit it, it may have a different, more grandiose format than /etc/sysconfig/network-scripts/ifcfg-eno1 but it describes the same thing. What would it take to make my own files? Long story short. not much:

[root@testbox ~]# more /etc/NetworkManager/system-connections/dmzbr.nmconnection /etc/NetworkManager/system-connections/DMZ.nmconnection 
::::::::::::::
/etc/NetworkManager/system-connections/dmzbr.nmconnection
::::::::::::::
[connection]
id=dmzBridge
type=bridge
interface-name=dmzbr

[ethernet]

[bridge]

[ipv4]
method=disabled

[ipv6]
addr-gen-mode=default
method=disabled

[proxy]
::::::::::::::
/etc/NetworkManager/system-connections/DMZ.nmconnection
::::::::::::::
[connection]
id=DMZ
type=vlan
interface-name=eno1.3
master=dmzbr
slave-type=bridge

[ethernet]

[vlan]
flags=1
id=3
parent=eno1

[bridge-port]
[root@testbox ~]# 

Note I did not even bother to declare the timestamp or uuid; the later can be created on the fly as the interface comes online. What does that mean? I can keep a copy of these files in a safe place, In fact, I have the following file (that is the filename, I swear)

[root@testbox ~]# cat NICs/eno1-oh_shit.nmconnection 
[connection]
id=eno1
uuid=9b1b155f-8197-3a7a-a9cc-79cab8b92da1
type=ethernet
autoconnect-priority=-999
interface-name=eno1

[ethernet]

[ipv4]
method=auto

[ipv6]
addr-gen-mode=eui64
method=auto

[proxy]
[root@testbox ~]# 

The idea of this file is if I screw the network up and need to get it back quickly, I copy that to /etc/NetworkManager/system-connections/eno1.nmconnection, make sure it is connected to an untagged vlan switchport, and then restart network manager. That will then get a dhcp IP address and off it goes.

What about Ansible?

The same way I created /etc/sysconfig/network-scripts/ifcfg-eno1.3 I can create these files; the template looks a bit different but everything else is similar. Heretic? Surely, but that is how I roll.

Wednesday, September 14, 2016

Setting up atftp in (ubuntu) Linux

I have a switch whose firmware was in dire need of being updated. Thing is, the switch will only take the upgrade firmware if it is offered by a tftp server, don't ask me why. And that is something I tend not to have running; last time I used one was to network boot Solaris boxes. Sounds like an excuse to write another article.

Just to be different I will be deploying this on a Ubuntu Linux host instead of a CentOS/RedHat one as I usually do. Why? Doing the same all the time gets boring quickly. So, let's see which versions of tftpd we can pick and choose:

raub@desktop:~$ apt-cache search tftpd
tftpd-hpa - HPA's tftp server
atftpd - advanced TFTP server
libnet-tftpd-perl - Perl extension for Trivial File Transfer Protocol Server
tftpd - Trivial file transfer protocol server
uec-provisioning-tftpd - the UEC Provisioning TFTP server
raub@desktop:~$

After careful scientific consideration, the atftpd one sounds more interesting (Multi-threaded TFTP server implementing extension and multicast), so we will pick that one. I think this is the part of the show in which we go through the steps to do the deed:

  1. A good place to start is to install it. I like command line and apt-get, so I think

    sudo apt-get install atftpd

    should do the trick. Of course you can use aptitude or the GUI. But I am lazy.

  2. Traditionally the directory used to put stuff that will be shared by tftp is /tftpboot, but current practice is to use /srv/tftp. In fact, atftpd does create /srv/tftp for you. For the same of showing how to customize things, let's say we want to be old school. And that means creating /tftpboot ourselves:

    sudo mkdir /tftpboot
    sudo chmod -R 777 /tftpboot
    sudo chown -R nobody:nogroup /tftpboot
  3. This might be a good time to put the files we want to be shared, in this example a file called image.bin in /tftpboot

  4. We need to configure it by editing /etc/default/atftpd. Here is what mine looks like

    raub@desktop:~$ cat /etc/default/atftpd
    USE_INETD=false
    OPTIONS="--tftpd-timeout 300 --retry-timeout 5 --port=69 --mcast-port 1758 
    --mcast-addr 192.168.0.0-255 --mcast-ttl 1 --maxthread 100 --verbose=7 /tftpboot"
    raub@desktop:~$

    where:

    • --port=69 we are forcing it to use the default tftp port
    • --mcast-addr 192.168.0.0-255 specifies the multicast address range we will be using. Being lazy, I am using the entire 192.168.0.0/24 range
    • /tftpboot is the directory we will be sharing as explained above. By default the config file specifies /srv/tftp which means if we put our file in /tftpboot we would get a message like

      Sep 13 13:54:00 desktop atftpd[28045]: File /srv/tftp/image.bin 
      not found

      when we try to fetch the file

    • --verbose=7 is the highest amount of verbose we can use. By default its value is set to 5.

    Once it starts properly (service atftpd start should be enough to start it), you should see something like

    raub@desktop:~$ ps -ef|grep ftp
    raub     16510 11504  0 15:26 pts/11   00:00:00 grep ftp
    nobody   28161     1  0 Sep13 ?        00:00:00 /usr/sbin/atftpd --daemon --tftpd-timeout 300 --retry-timeout 5 --port=69 --mcast-port 1758 --mcast-addr 192.168.0.0-255 --mcast-ttl 1 --maxthread 100 --verbose=7 /tftpboot
    raub@desktop:~$

How to get the file using tftp is beyond this discussion because it depends on your OS and the tftp client you are using. For instance the switch might show a webpage where you can configure the name of the tftp server -- 192.168.0.102 in my example -- and the name of the file you want to grab. What is more interesting is to see how the entire enchilada from we started the tftp server until we get the file image.bin looks like. By default (can be configured) we would see that in /var/log/syslog:

Sep 13 13:55:52 desktop systemd[1]: Starting LSB: Launch atftpd server...
Sep 13 13:55:52 desktop atftpd[28160]: Advanced Trivial FTP server started (0.7)
Sep 13 13:55:52 desktop atftpd[28153]: Starting Advanced TFTP server: atftpd.
Sep 13 13:55:52 desktop atftpd[28161]:   running in daemon mode on port 69
Sep 13 13:55:52 desktop atftpd[28161]:   logging level: 7
Sep 13 13:55:52 desktop atftpd[28161]:   directory: /tftpboot/
Sep 13 13:55:52 desktop atftpd[28161]:   user: nobody.nogroup
Sep 13 13:55:52 desktop atftpd[28161]:   log file: syslog
Sep 13 13:55:52 desktop atftpd[28161]:   not forcing to listen on local interfaces.
Sep 13 13:55:52 desktop atftpd[28161]:   server timeout: Not used
Sep 13 13:55:52 desktop atftpd[28161]:   tftp retry timeout: 5
Sep 13 13:55:52 desktop atftpd[28161]:   maximum number of thread: 100
Sep 13 13:55:52 desktop atftpd[28161]:   option timeout:   enabled
Sep 13 13:55:52 desktop atftpd[28161]:   option tzise:     enabled
Sep 13 13:55:52 desktop atftpd[28161]:   option blksize:   enabled
Sep 13 13:55:52 desktop atftpd[28161]:   option multicast: enabled
Sep 13 13:55:52 desktop atftpd[28161]:      address range: 192.168.0.0-255
Sep 13 13:55:52 desktop atftpd[28161]:      port range:    1758
Sep 13 13:55:52 desktop systemd[1]: Started LSB: Launch atftpd server.
Sep 13 13:55:59 desktop atftpd[28161]: socket may listen on any address, including broadcast
Sep 13 13:55:59 desktop atftpd[28161]: Creating new socket: 192.168.0.102:45115
Sep 13 13:55:59 desktop atftpd[28161]: Serving image.bin to 192.168.0.3:2295
Sep 13 13:56:03 desktop atftpd[28161]: End of transfer
Sep 13 13:56:03 desktop atftpd[28161]: Server thread exiting

The underline on the entry when the file image.bin is transferred was added to make it easy to see. And that is pretty much all I had to do. Once file was transfered, I stopped atftpd and then remove it

sudo apt-get remove --purge atftpd

because I do not like to have unused services running.

PS: Always backup your switch/network device's config before upgrading its firmware in case it reverts to default as part of the upgrade process. Guess who forgot to do that? Good thing I had good notes and could reconfigure it using the time-honored cut-n-paste technique

Friday, April 29, 2016

My Introduction to firewallD

WARNING:

  1. When I say My Introduction, I do not mean I am trying to introduce you to firewallD. I really mean this is the first time I had to properly deal with it. I wish this story would involve robots, but it is not that exciting

  2. You should not follow this blindly as a guide. I screwed it up and documented it here. Read it first and see what I did wrong and why.

So I am building an apache webserver on a Linux box (Red Hat Enterprise Server 7.2 if you are curious, but there is nothing stopping the gist of my story working on any Linux distro using systemd and, more specifically, firewallD) and want to limit the number of hosts that can access it to one, which happens to have 192.168.79.13 as its IP.

In the golden days...

If we were to use dear ol' pedestrian iptables, one way to create the rules would be

iptables -I INPUT -s '192.168.79.13/32' -m state --state NEW \
   -m tcp -p tcp --dport 80 -j ACCEPT
iptables -I INPUT -s '192.168.79.13/32' -m state --state NEW \
   -m tcp -p tcp --dport 443 -j ACCEPT

and then check if it has been loaded using

iptables -L -n

At this point we might have decided we really should have also told it to apply the rule to only a specific interface or IP (we might be listening to many IPs on the same interface). So we would adjust it as needed until we are satisfied. And, once satisfied it works, save it using

service iptables save

Modern Times

So far so good. But in Red Hat 7 and CentOS 7 as a result, iptables save is no longer an available command. firewallD should be used instead, which is amusing since it is but a front end to iptables. I guess the point is that if iptables is replaced you would still be able to use firewallD to control whatever comes next; in other words, it is an abstraction layer.

But, I digress. Bottom line is we need to figure out how to use firewallD. First thing is that we will be using firewall-cmd to talk to it. Next is that it allows to create separate firewall zones (same idea as the Windows firewall zones for the Microsoft crowd amongst you) which define the interface and possibly IPs it is associated with and which services and rules are supposed to do what there. And, yes, that last sentence was a mouthful, so we shall begin by seeing which zone we are using.

[user@webtest httpd]$ sudo firewall-cmd --get-default-zone
public
[user@webtest httpd]$

I guess that makes sense since this is a pretty much out-of-the-box install; we are still customizing it. But, which zones are in use?

[user@webtest httpd]$ sudo firewall-cmd --get-active-zones
public
  interfaces: eth0
[user@webtest httpd]$

So, the only active zone is the default one, which is being applied to eth0. What can you tell me about the public zone?

[user@webtest httpd]$ sudo firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: eth0
  sources:
  services: dhcpv6-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

[user@webtest httpd]$

I knew about ssh service (sshd) -- that is how I am connected to it -- but I did not realize it actually has a dhcpv6 client. How about ports in use?

[user@webtest httpd]$ sudo firewall-cmd --zone=public --list-ports
[user@webtest httpd]$

Hmmm, nothing? But sshd uses port 22 by default (which is how it is set right now); shouldn't it show up? Well, like Windows' firewall you can specify a service or just a port. If you do the former, its port is not listed. I wonder if it is just satisfied the ssh service is on and relies on it to know which port it is using; this sounds like something worthwhile to test later. Talking about services, which ones firewallD knows of, be them running or not?

[user@webtest httpd]$ sudo firewall-cmd --get-services
RH-Satellite-6 amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client 
dns freeipa-ldap freeipa-ldaps freeipa-replication ftp high-availability http 
https imaps ipp ipp-client ipsec iscsi-target kerberos kpasswd ldap ldaps 
libvirt libvirt-tls mdns mountd ms-wbt mysql nfs ntp openvpn pmcd pmproxy 
pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind rsyncd samba 
samba-client smtp ssh telnet tftp tftp-client transmission-client vdsm 
vnc-server wbem-https
[user@webtest httpd]$

It even knows about telnet! Not that it is even installed here, which means firewallD has some kind of configuration file somewhere listing all the services it should be aware of. Which means you can add/subtract services to this list... or it ends up overwritten during an upgrade. One of the two, right?

So we created the firewall zone. How does it look like? It is actually a xml file; here's the one for the public zone as it stands right now in this conversation:

[user@webtest httpd]$ sudo cat /etc/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other 
computers on networks to not harm your computer. Only selected incoming 
connections are accepted.</description>
  <service name="dhcpv6-client"/>
  <service name="ssh"/>
</zone>
[user@webtest httpd]$

I think we have an idea of the beast.

Houston, we have a problem

So, let's say we want to allow access to our webserver on port 80. We can do something like

firewall-cmd --zone=public --add-port=80/tcp --permanent

or

firewall-cmd --zone=public --add-service=http --permanent

right? Er, not quite. Let me show what I mean: after we run one of those commands (I picked the service one) we should always check if the zone was updated.

[user@webtest httpd]$ sudo firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: eth0
  sources:
  services: dhcpv6-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

[user@webtest httpd]$

But yet, it is in the file!

[user@webtest httpd]$ sudo cat /etc/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other 
computers on networks to not harm your computer. Only selected incoming 
connections are accepted.</description>
  <service name="dhcpv6-client"/>
  <service name="ssh"/>
  <service name="http"/>
</zone>
[user@webtest httpd]$

What is going on? Short version: user error. Long version: --permanent means save to file only. It does not affect the running version. If you want to turn it on, you need to restart firewalld.

[user@webtest httpd]$ sudo systemctl restart firewalld
[user@webtest httpd]$

Another alternative is not to use --permanent, as in

firewall-cmd --zone=public --add-service=http

That will commit the change immediately. If you reboot it is lost, so it is a good way to test it. And then once you are ready to commit, you can then do

firewall-cmd --runtime-to-permanent

which as the name implies saves the running firewall to the appropriate zone files. In any case, here is the outcome:

[user@webtest httpd]$ sudo firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: eth0
  sources:
  services: dhcpv6-client httpd ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

[user@webtest httpd]$

If you want to remove that very rule, you can do it by

firewall-cmd --zone=public --remove-service=httpd

Back to the iptables example

Do you remember it? No? Let me put it here

iptables -I INPUT -s '192.168.79.13/32' -m state --state NEW \
   -m tcp -p tcp --dport 80 -j ACCEPT
iptables -I INPUT -s '192.168.79.13/32' -m state --state NEW \
   -m tcp -p tcp --dport 443 -j ACCEPT

The key here is not we are specifying the ports, but that we want to specify the IP traffic is coming from. In the firewalld universe that requires a rich rule. Here is what I am using:

firewall-cmd --zone=public --add-rich-rule="rule family="ipv4" \
    source address="192.168.79.13/32" service name="http" accept"
firewall-cmd --zone=public --add-rich-rule="rule family="ipv4" \
    source address="192.168.79.13/32" service name="https" accept"

it should behave the very same way as the iptables entries. But, let's see how it looks like in runtime

[user@webtest httpd]$ sudo firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: eth0
  sources:
  services: dhcpv6-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
        rule family="ipv4" source address="192.168.79.13/32" service 
name="https" accept
        rule family="ipv4" source address="192.168.79.13/32" service 
name="http" accept
[user@webtest httpd]$

And, after we are satisfied and committed it, in the zone file

[user@webtest httpd]$ sudo cat /etc/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
  <short>Public</short>
  <description>For use in public areas. You do not trust the other 
computers on networks to not harm your computer. Only selected incoming 
connections are accepted.</description>
  <service name="dhcpv6-client"/>
  <service name="ssh"/>
  <rule family="ipv4">
    <source address="192.168.79.13/32"/>
    <service name="https"/>
    <accept/>
  </rule>
  <rule family="ipv4">
    <source address="192.168.79.13/32"/>
    <service name="http"/>
    <accept/>
  </rule>
</zone>
[user@webtest httpd]$

References