One place for hosting & domains

      Client

      Comment installer et configurer un démon et un client SNMP sur Ubuntu 18.04


      L’auteur a choisi Internet Archive pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Le rôle d’un administrateur système consiste en grande partie à recueillir des informations précises sur les serveurs et l’infrastructure. Il existe un certain nombre d’outils et d’options pour la collecte et le traitement de ce type d’informations. Beaucoup d’entre eux sont construits sur la base d’une technologie appelée SNMP.

      SNMP est l’abréviation de simple network management protocol (protocole simple de gestion de réseau). C’est un moyen pour les serveurs de partager des informations sur leur état actuel, et aussi un canal par lequel un administrateur peut modifier des valeurs prédéfinies. Si le protocole lui-même est léger, la structure des programmes qui implémentent SNMP peut rapidement gagner en complexité. Pour plus d’informations sur les bases du protocole SNMP, consultez notre article Introduction au SNMP.

      Dans ce guide, vous allez configurer les outils pour communiquer en utilisant le SNMP. Vous utiliserez deux serveurs Ubuntu 18.04 pour la démonstration. L’un contiendra le manager SNMP, qui parle à l’agent pour implémenter des dispositifs réseau. Il sera appelé manager server. L’autre serveur contiendra l’agent SNMP, qui agira sur les ordres du manager server. Il sera appelé agent server. Vous pouvez également choisir d’installer l’agent sur la machine manager, mais en les gardant séparés, il est plus facile de démontrer les fonctionnalités fournies par chaque composant.

      Conditions préalables

      Pour suivre ce tutoriel, vous aurez besoin de :

      Étape 1 – Installation du démon et des utilitaires SNMP

      Vous pouvez commencer à explorer comment le SNMP peut être implémenté sur un système en installant le démon et les outils sur vos serveurs Ubuntu.

      Depuis votre machine locale, connectez-vous au manager server en tant qu’utilisateur non root :

      • ssh your_username@manager_server_ip_address

      Mettez à jour l’index des paquets pour le gestionnaire de paquets APT :

      Ensuite, installez le logiciel SNMP :

      • sudo apt install snmp snmp-mibs-downloader

      Le paquet snmp fournit un ensemble d’outils en ligne de commande pour émettre des requêtes SNMP aux agents. Le paquet snmp-mibs-downloader aidera à installer et à gérer les fichiers MIB (Management Information Base), qui gardent la trace des objets du réseau.

      Ensuite, ouvrez un nouveau terminal sur votre machine locale et connectez-vous à l’agent server :

      • ssh your_username@agent_server_ip_address

      Sur l’agent server, mettez à jour l’index des paquets :

      Ensuite, installez le démon SNMP :

      Notez que vous n’avez pas besoin du paquet snmp-mibs-downloader, puisque l’agent server ne gérera pas de fichiers MIB.

      Maintenant que vous avez installé ces composants, vous allez configurer votre manager server.

      Étape 2 – Configuration du manager server SNMP

      Comme mentionné précédemment, la majeure partie du travail se fait dans l’agent server, de sorte que votre configuration sur le manager server sera plus simple. Il vous suffit de modifier un fichier pour vous assurer que les outils SNMP peuvent utiliser les données MIB supplémentaires que vous avez installées.

      Sur votre manager server, ouvrez le fichier /etc/snmp/snmp.conf dans votre éditeur de texte avec des privilèges sudo. Ce tutoriel utilisera nano :

      • sudo nano /etc/snmp/snmp.conf

      Dans ce fichier, il y a quelques commentaires et une seule ligne non commentée. Pour permettre au manager d’importer les fichiers MIB, commentez la ligne mibs : :

      /etc/snmp/snmp.conf

      # As the snmp packages come without MIB files due to license reasons, loading
      # of MIBs is disabled by default. If you added the MIBs you can reenable
      # loading them by commenting out the following line.
      #mibs :
      

      Enregistrez et fermez snmp.conf en appuyant sur CTRL+X, suivi de Y, puis ENTER (ENTRÉE) si vous utilisez nano.

      Vous avez maintenant terminé la configuration du manager server, mais vous devrez encore utiliser ce serveur pour vous aider à configurer votre agent server, ce que vous ferez à l’étape suivante.

      Étape 3 – Configuration de l’agent server SNMP

      En tant que véritable système client-serveur, l’agent server ne dispose d’aucun des outils externes nécessaires pour effectuer sa propre configuration SNMP. Vous pouvez modifier certains fichiers de configuration pour effectuer quelques changements, mais la plupart des changements que vous devez effectuer se feront en vous connectant à votre agent server à partir de votre manager server.

      Dans ce tutoriel, vous utiliserez la version 3 du protocole SNMP. Contrairement à SNMPv1 et v2, dans SNMPv3 chaque message contient des paramètres de sécurité qui sont codés. Dans cette étape, vous allez configurer les règles d’authentification et de contrôle d’accès SNMPv3.

      Pour commencer, sur votre agent server, ouvrez le fichier de configuration du démon avec des privilèges sudo :

      • sudo nano /etc/snmp/snmpd.conf

      À l’intérieur, vous devrez effectuer quelques modifications. Elles seront principalement utilisées pour bootstrapper votre configuration afin que vous puissiez la gérer à partir de votre autre serveur.

      Tout d’abord, vous devez modifier la directive agentAdress. Actuellement, elle est réglée pour n’autoriser que les connexions provenant de l’ordinateur local. Vous devrez commenter la ligne actuelle, et décommenter la ligne en dessous, qui autorise toutes les connexions.

      /etc/snmp/snmpd.conf

      #  Listen for connections from the local system only
      #agentAddress  udp:127.0.0.1:161
      #  Listen for connections on all interfaces (both IPv4 *and* IPv6)
      agentAddress udp:161,udp6:[::1]:161
      

      Remarque : étant donné qu’autoriser toutes les connexions n’est pas une bonne pratique en termes de sécurité, il est préférable de reverrouiller les connexions rapidement une fois le bootstrap terminé.

      Ensuite, vous insérerez temporairement une ligne createUser. Ces directives ne sont normalement pas conservées dans ce fichier ; vous allez les supprimer à nouveau dans un instant.

      L’utilisateur que vous créez sera appelé bootstrap et sera utilisé comme modèle pour créer votre premier utilisateur réel. Les paquets SNMP le font par un processus de clonage des propriétés de l’utilisateur.

      Lorsque vous définissez un nouvel utilisateur, vous devez spécifier le type d’authentification (MD5 ou SHA) et fournir une phrase de passe d’au moins huit caractères. Si vous prévoyez d’utiliser le cryptage pour le transfert, comme vous le ferez dans ce tutoriel, vous devez également préciser le protocole de confidentialité (DES ou AES) et, éventuellement, une phrase de passe de protocole de confidentialité. Si aucune phrase de passe de protocole de confidentialité n’est fournie, la phrase de passe d’authentification sera également utilisée pour le protocole de confidentialité.

      Ajoutez cette ligne createUser à la fin du fichier :

      /etc/snmp/snmpd.conf

      ...
      createUser bootstrap MD5 temp_password DES
      

      Maintenant que vous avez un nouvel utilisateur spécifié, vous pouvez configurer le niveau d’accès de cet utilisateur. Dans ce tutoriel, vous le configurerez pour votre utilisateur bootstrap, mais aussi pour le nouvel utilisateur que vous allez créer, appelé demo. Vous leur permettrez l’accès en lecture et en écriture en utilisant la directive rwuser (l’alternative est rouser pour l’accès en lecture seule).

      Vous imposerez également l’utilisation du cryptage en spécifiant priv après votre utilisateur. Si vous souhaitez restreindre l’utilisateur à une partie spécifique de la MIB, vous pouvez spécifier à la fin de la ligne l’identificateur de l’objet (OID) de plus haut niveau auquel l’utilisateur doit avoir accès.

      Pour les besoins de ce tutoriel, vos deux lignes seront les suivantes :

      /etc/snmp/snmpd.conf

      ...
      rwuser bootstrap priv
      rwuser demo priv
      

      Lorsque vous avez terminé d’effectuer ces modifications, enregistrez et fermez le fichier.

      Pour implémenter ces modifications, redémarrez le service snmpd sur votre agent server :

      • sudo systemctl restart snmpd

      Le démon SNMP écoutera les connexions sur le port :161. Configurez UFW pour autoriser les connexions depuis le manager server vers ce port :

      • sudo ufw allow from manager_server_ip_address to any port 161

      Pour savoir plus sur UFW, consultez le tutoriel Comment configurer un pare-feu avec UFW sur Ubuntu 18.04.

      Maintenant que l’agent server est configuré, vous pouvez vous connecter à votre agent server depuis le manager server pour vérifier la connexion.

      Étape 4 – Vérification de l’authentification par l’agent server

      Dans cette étape, vous allez effectuer un test pour vous assurer que vous pouvez vous connecter à l’agent server avec votre compte bootstrap. Avant cela, cependant, ce tutoriel abordera un peu la structure générale de l’envoi d’une commande SNMP.

      Lorsque vous utilisez la suite d’outils incluse dans le paquet snmp (la suite logicielle net-snmp), la façon dont vous devez appeler les commandes est régie par quelques règles. La première chose à faire est de s’authentifier auprès du démon SNMP avec lequel vous souhaitez communiquer. Cela implique généralement de fournir quelques informations. Les plus courantes sont les suivantes :

      • -v : cet indicateur est utilisé pour spécifier la version du protocole SNMP que vous souhaitez utiliser. Ce tutoriel utilisera v3.
      • -c : cet indicateur est utilisé si vous utilisez des chaînes communautaires de type SNMP v1 ou v2 pour l’authentification. Comme vous utilisez une authentification basée sur l’utilisateur de type v3, vous n’avez pas besoin de cet indicateur.
      • -u : ce paramètre est utilisé pour spécifier le nom d’utilisateur avec lequel vous souhaitez vous identifier. Pour lire ou modifier quoi que ce soit en utilisant le SNMP, vous devez vous authentifier avec un nom d’utilisateur connu.
      • -l : ceci est utilisé pour spécifier le niveau de sécurité avec lequel vous vous connectez. Les valeurs possibles sont noAuthNoPriv pour l’absence d’authentification et de cryptage, authNoPriv pour l’authentification mais pas de cryptage, et authPriv pour l’authentification et le cryptage. Le nom d’utilisateur que vous utilisez doit être configuré pour fonctionner au niveau de sécurité que vous spécifiez, sinon l’authentification ne réussira pas.
      • -a : ce paramètre est utilisé pour spécifier le protocole d’authentification qui est utilisé. Les valeurs possibles sont MD5 ou SHA. Cela doit correspondre aux informations qui ont été spécifiées lorsque l’utilisateur a été créé.
      • -x : ce paramètre permet de spécifier le protocole de cryptage qui est utilisé. Les valeurs possibles sont DES ou AES. Cela doit correspondre aux informations qui ont été spécifiées lorsque l’utilisateur a été créé. Ce paramètre est nécessaire chaque fois que priv est indiqué après la spécification de privilège de l’utilisateur, rendant le cryptage obligatoire.
      • -A : ceci est utilisé pour donner la phrase de passe d’authentification qui a été spécifiée lors de la création de l’utilisateur.
      • -X : il s’agit de la phrase de passe de cryptage qui a été spécifiée lors de la création de l’utilisateur. Si aucune n’a été spécifiée mais qu’un algorithme de cryptage a été donné, la phrase de passe d’authentification sera utilisée. Ceci est nécessaire lorsque le paramètre -x est donné ou lorsque la spécification de privilège d’un utilisateur est suivie d’un priv, nécessitant un cryptage.

      À l’aide de ces informations, vous pouvez construire vos commandes. Étant donné la configuration de votre utilisateur bootstrap, les commandes que vous utiliserez avec ce compte ressembleront à ça :

      snmp_command -u bootstrap -l authPriv -a MD5 -x DES -A temp_password -X temp_password remote_host snmp_sub_command_or_options
      

      À partir de votre manager server, effectuez un test pour vous assurer que votre compte bootstrap est disponible. Tapez ce qui suit pour afficher les informations système relatives à l’agent server :

      • snmpget -u bootstrap -l authPriv -a MD5 -x DES -A temp_password -X temp_password agent_server_ip_address 1.3.6.1.2.1.1.1.0

      La chaîne 1.3.6.1.2.1.1.1.0 est l’OID qui est responsable de l’affichage des informations du système. Elle renverra la sortie de uname -a sur le système à distance.

      Cela donnera le résultat suivant :

      Output

      SNMPv2-MIB::sysDescr.0 = STRING: Linux agent 4.15.0-66-generic #75-Ubuntu SMP Tue Oct 1 05:24:09 UTC 2019 x86_64

      Maintenant que vous avez vérifié que vous pouvez vous authentifier auprès du serveur qui exécute le démon SNMP, vous pouvez créer votre compte utilisateur ordinaire.

      Étape 5 – Configuration du compte utilisateur ordinaire

      Bien que vous ayez spécifié les privilèges du compte utilisateur demo dans le fichier snmpd.conf, vous n’avez pas encore créé cet utilisateur. Dans cette étape, vous allez utiliser l’utilisateur bootstrap comme modèle pour votre nouvel utilisateur. Pour ce faire, vous utiliserez l’outil snmpusm, qui sert à la gestion des utilisateurs.

      Sur le manager server, vous pouvez créer l’utilisateur à partir du modèle en utilisant l’outil snmpusm et la syntaxe générale suivante :

      snmpusm authentication_info agent_server_ip_address create new_user existing_user
      

      En utilisant ce que vous savez sur les indicateurs d’authentification que vous devez passer, et en exploitant le compte d’utilisateur que vous avez déjà (bootstrap), vous pouvez créer un utilisateur qui correspond aux privilèges d’utilisateur que vous avez déjà définis (demo).

      La commande ressemblera à ceci :

      • snmpusm -u bootstrap -l authPriv -a MD5 -x DES -A temp_password -X temp_password agent_server_ip_address create demo bootstrap

      Vous recevrez le message suivant :

      Output

      User successfully created.

      Vous disposez maintenant d’un utilisateur pleinement fonctionnel appelé demo sur votre agent server. Toutefois, il utilise toujours les mêmes informations d’authentification que le compte bootstrap. Pour augmenter la sécurité, vous pouvez modifier le mot de passe. Cette fois, vous utiliserez le compte demo pour vous authentifier. N’oubliez pas que les mots de passe doivent contenir au moins huit caractères :

      • snmpusm -u demo -l authPriv -a MD5 -x DES -A temp_password -X temp_password agent_server_ip_address passwd temp_password new_password

      Vous recevrez le message suivant :

      Output

      SNMPv3 Key(s) successfully changed.

      Vous pouvez tester vos nouveaux identifiants et mot de passe en demandant à l’agent server depuis combien de temps le service SNMP fonctionne. Vous utiliserez la commande snmpget pour obtenir une seule valeur depuis l’agent server.

      Cette fois, profitez des définitions MIB supplémentaires que vous avez téléchargées pour demander la valeur par nom au lieu de l’identifiant numérique OID.

      • snmpget -u demo -l authPriv -a MD5 -x DES -A new_password -X new_password agent_server_ip_address sysUpTime.0

      Vous récupérerez une valeur qui représente la dernière fois que le démon SNMP distant a été redémarré :

      Output

      DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (53309) 0:08:53.09

      Vous disposez maintenant d’un compte utilisateur qui fonctionne nommé demo. Dans l’étape suivante, vous allez simplifier le travail avec les commandes SNMP en configurant le client.

      Étape 6 – Création d’un fichier de configuration client

      Vous avez probablement remarqué à ce stade que les détails d’authentification pour toutes vos commandes SNMP seront assez statiques à chaque requête. Au lieu de les saisir à chaque fois, vous pouvez créer un fichier de configuration côté client qui contiendra les informations d’identification avec lesquelles vous vous connectez.

      Le fichier de configuration client peut être placé à deux endroits différents en fonction de la diffusion que vous souhaitez lui donner.

      Si vous souhaitez partager vos identifiants de connexion avec tout utilisateur valide sur votre machine de gestion, vous pouvez placer vos détails de configuration dans le fichier global snmp.conf sur le manager server. Vous devez ouvrir ce fichier avec les privilèges sudo :

      • sudo nano /etc/snmp/snmp.conf

      Toutefois, si vous souhaitez définir les données d’authentification pour votre seul utilisateur, vous pouvez créer un répertoire .snmp caché dans le répertoire de base de votre utilisateur sur le manager server, et y créer le fichier :

      • mkdir ~/.snmp
      • nano ~/.snmp/snmp.conf

      Quelle que soit votre décision quant à l’emplacement de votre configuration, le contenu sera le même.

      Les commandes que vous utiliserez pour vous authentifier figurent dans le tableau suivant. Dans la colonne de droite, vous pouvez voir les noms des directives utilisées pour définir les informations de configuration dans le fichier snmp.conf :

      Indicateur de commande Description Directive snmp.conf traduite
      -u username Le nom d’utilisateur SNMPv3 avec lequel vous devez vous identifier. defSecurityName username
      -l authPriv Le niveau de sécurité pour vous authentifier. defSecurityLevel authPriv
      -a MD5 Le protocole d’authentification à utiliser. defAuthType MD5
      -x DES Le protocole de confidentialité (cryptage) à utiliser. defPrivType DES
      -A passphrase La phrase de passe d’authentification pour le nom d’utilisateur fourni. defAuthPassphrase passphrase
      -X passphrase La phrase de passe de confidentialité pour le nom d’utilisateur fourni. defPrivPassphrase passphrase

      En utilisant ces informations, vous pouvez construire un fichier snmp.conf approprié. Pour ce guide, il ressemblera à ceci :

      snmp.conf

      defSecurityName demo
      defSecurityLevel authPriv
      defAuthType MD5
      defPrivType DES
      defAuthPassphrase new_password
      defPrivPassphrase new_password
      

      Lorsque vous avez terminé, enregistrez et fermez le fichier.

      Maintenant, vous pouvez passer des commandes sans fournir les informations d’authentification. Vous n’aurez besoin que de la commande SNMP, de l’hôte et des arguments de commande.

      Au lieu de saisir :

      • snmpget -u demo -l authPriv -a MD5 -x DES -A new_password -X new_password agent_server_ip_address sysUpTime.0

      Vous pouvez saisir :

      • snmpget agent_server_ip_address sysUpTime.0

      Comme vous pouvez le constater, cela réduit considérablement la quantité d’informations que vous devez fournir dans chaque demande. Ensuite, vous supprimerez le compte bootstrap pour renforcer la sécurité du réseau.

      Étape 7 – Suppression du compte bootstrap

      Maintenant que votre compte ordinaire est correctement configuré, vous pouvez supprimer le compte bootstrap non sécurisé.

      Sur votre agent server, ouvrez à nouveau le fichier /etc/snmp/snmpd.conf avec des privilèges sudo.

      • sudo nano /etc/snmp/snmpd.conf

      Trouvez et commentez (ou supprimez) les deux lignes que vous avez ajoutées précédemment et qui font référence à l’utilisateur bootstrap:

      /etc/snmp/snmpd.conf

      ...
      #createUser bootstrap MD5 temp_password DES
      #rwuser bootstrap priv
      ...
      

      Enregistrez et fermez le fichier.

      Maintenant, redémarrez le démon SNMP :

      • sudo systemctl restart snmpd

      Cela permettra de respecter la recommandation de ne pas avoir de directives createUser dans le fichier snmpd.conf normal. Cela supprimera également les privilèges de cet utilisateur temporaire.

      Si vous souhaitez supprimer complètement l’utilisateur bootstrap de la usmUserTable, vous pouvez le faire en émettant cette commande depuis le manager server :

      • snmpusm agent_server_ip_address delete bootstrap

      Vous recevrez la réponse suivante :

      Output

      User successfully deleted.

      Conclusion

      À ce stade, vous disposez d’une installation client-serveur entièrement configurée qui peut communiquer de manière sécurisée en utilisant le protocole SNMP. Vous pouvez désormais ajouter des démons supplémentaires sur d’autres hôtes et configurer l’accès aux comptes sur l’ensemble de votre infrastructure.

      Pour en savoir plus, vous pouvez utiliser notre tutoriel Comment utiliser la suite d’outils Net-SNMP pour gérer et surveiller les serveurs : vous vous familiariserez avec les outils SNMP et apprendrez à les utiliser pour récupérer des valeurs une par une ou par lot, et à modifier les données.



      Source link

      How To Install and Configure an SNMP Daemon and Client on Ubuntu 18.04


      The author selected the Internet Archive to receive a donation as part of the Write for DOnations program.

      Introduction

      A large part of being a system administrator is collecting accurate information about your servers and infrastructure. There are a number of tools and options for gathering and processing this type of information. Many of them are built upon a technology called SNMP.

      SNMP stands for simple network management protocol. It is a way that servers can share information about their current state, and also a channel through which an administer can modify pre-defined values. While the protocol itself is lightweight, the structure of programs that implement SNMP can quickly grow in complexity. For more information on the basics of the SNMP protocol, see our An Introduction to SNMP article.

      In this guide, you will set up the tools to communicate using SNMP. You will be using two Ubuntu 18.04 servers to demonstrate. One will contain the SNMP manager, which will talk to the agent to implement network devices. This will be called the manager server. The other server will have the SNMP agent, which will act on the orders from the manager server. This will be called the agent server. You could choose to install the agent on the manager machine as well, but keeping them separate makes it easier to demonstrate what functionality is provided by each component.

      Prerequisites

      To follow this tutorial, you will need:

      Step 1 — Installing the SNMP Daemon and Utilities

      You can begin to explore how SNMP can be implemented on a system by installing the daemon and tools on your Ubuntu servers.

      From your local machine, log into the manager server as your non-root user:

      • ssh your_username@manager_server_ip_address

      Update the package index for the APT package manager:

      Next, install the SNMP software:

      • sudo apt install snmp snmp-mibs-downloader

      The snmp package provides a collection of command line tools for issuing SNMP requests to agents. The snmp-mibs-downloader package will help to install and manage Management Information Base (MIB) files, which keep track of network objects.

      Then, open up a new terminal on your local machine and log into the agent server:

      • ssh your_username@agent_server_ip_address

      On the agent server, update the package index:

      Then, install the SNMP daemon

      Note that you do not need the snmp-mibs-downloader package, since the agent server will not be managing MIB files.

      Now that you have installed these components, you will configure your manager server.

      Step 2 — Configuring the SNMP Manager Server

      As mentioned before, most of the bulk of the work happens in the agent server, so your configuration on the manager server will be less involved. You just need to modify one file to make sure that SNMP tools can use the extra MIB data you installed.

      On your manager server, open the /etc/snmp/snmp.conf file in your text editor with sudo privileges. This tutorial will use nano:

      • sudo nano /etc/snmp/snmp.conf

      In this file, there are a few comments and a single un-commented line. To allow the manager to import the MIB files, comment out the mibs : line:

      /etc/snmp/snmp.conf

      # As the snmp packages come without MIB files due to license reasons, loading
      # of MIBs is disabled by default. If you added the MIBs you can reenable
      # loading them by commenting out the following line.
      #mibs :
      

      Save and close snmp.conf by pressing CTRL+X, followed by Y, and then ENTER if you’re using nano.

      You are now finished configuring the manager server, but you will still need to use this server to help configure your agent server, which you will do in the next step.

      Step 3 — Configuring the SNMP Agent Server

      As a true client-server system, the agent server does not have any of the external tools needed to configure its own SNMP setup. You can modify some configuration files to make some changes, but most of the changes you need to make will be done by connecting to your agent server from your management server.

      In this tutorial, you will use version 3 of the SNMP protocol. Unlike SNMPv1 and v2, in SNMPv3 each message contains security parameters that are encoded. In this step you will configure SNMPv3 authentication and access control rules.

      To get started, on your agent server, open the daemon’s configuration file with sudo privileges:

      • sudo nano /etc/snmp/snmpd.conf

      Inside, you will have to make a few changes. These will mainly be used to bootstrap your configuration so that you can manage it from your other server.

      First, you need to change the agentAddress directive. Currently, it is set to only allow connections originating from the local computer. You’ll need to comment out the current line, and uncomment the line underneath, which allows all connections.

      /etc/snmp/snmpd.conf

      #  Listen for connections from the local system only
      #agentAddress  udp:127.0.0.1:161
      #  Listen for connections on all interfaces (both IPv4 *and* IPv6)
      agentAddress udp:161,udp6:[::1]:161
      

      Note: Since allowing all connections like this is not a security best practice, it is best to make sure to lock this back down soon, after the bootstraping is complete.

      Next, you will temporarily insert a createUser line. These directives are not normally kept in this file; you will be removing it again in a moment.

      The user you are creating will be called bootstrap and will be used as a template in which to create your first actual user. The SNMP packages do this through a process of cloning the user’s properties.

      When defining a new user, you must specify the authentication type (MD5 or SHA) as well as supply a passphrase that must be at least eight characters. If you plan on using encryption for the transfer, like you will in this tutorial, you also must specify the privacy protocol (DES or AES) and optionally a privacy protocol passphrase. If no privacy protocol passphrase is supplied, the authentication passphrase will be used for the privacy protocol as well.

      Add this createUser line to the end of the file:

      /etc/snmp/snmpd.conf

      ...
      createUser bootstrap MD5 temp_password DES
      

      Now that you have a new user specified, you can set up the level of access that this user will have. In this tutorial you will set this up for your bootstrap user, and also for the new user you will be creating, called demo. You will allow them read and write access by using the rwuser directive (the alternative is rouser for read-only access).

      You will also enforce the use of encryption by specifying priv after your user. If you wanted to restrict the user to a specific part of the MIB, you could specify the highest-level object identifier (OID) that the user should have access to at the end of the line.

      For this tutorial’s purposes, both of your lines will be as follows:

      /etc/snmp/snmpd.conf

      ...
      rwuser bootstrap priv
      rwuser demo priv
      

      When you are finished making these changes, save and close the file.

      To implement these changes, restart the snmpd service on your agent server:

      • sudo systemctl restart snmpd

      The SNMP daemon will listen for connections on port :161. Configure UFW to allow connections from the manager server to this port:

      • sudo ufw allow from manager_server_ip_address to any port 161

      You can learn more about UFW in How To Set Up a Firewall with UFW on Ubuntu 18.04.

      Now that the agent server is configured, you can connect to your agent server from the manager server to verify the connection.

      Step 4 — Verifying Authentication to the Agent Server

      In this step, you will test to make sure you can connect with your bootstrap account to the agent server. Before that, however, this tutorial will talk a bit about the general structure of sending an SNMP command.

      When using the suite of tools included in the snmp package (the net-snmp software suite), there are a few patterns in the way you must call the commands. The first thing to do is authenticate with the SNMP daemon that you wish to communicate with. This usually involves supplying a few pieces of information. The common ones are as follows:

      • -v: This flag is used to specify the version of the SNMP protocol that you would like to use. This tutorial will be using v3.
      • -c: This flag is used if you are using SNMP v1 or v2-style community strings for authentication. Since you are using v3-style user-based authentication, you don’t need to do this.
      • -u: This parameter is used to specify the username that you wish to authenticate as. To read or modify anything using SNMP, you must authenticate with a known username.
      • -l: This is used to specify the security level that you are connecting with. The possible values are noAuthNoPriv for no authentication and no encryption, authNoPriv for authentication but no encryption, and authPriv for authentication and encryption. The username that you are using must be configured to operate at the security level you specify, or else the authentication will not succeed.
      • -a: This parameter is used to specify the authentication protocol that is used. The possible values are MD5 or SHA. This must match the information that was specified when the user was created.
      • -x: This parameter is used to specify the encryption protocol that is used. The possible values are DES or AES. This must match the information that was specified when the user was created. This is necessary whenever the user’s privilege specification has priv after it, making encryption mandatory.
      • -A: This is used to give the authentication passphrase that was specified when the user was created.
      • -X: This is the encryption passphrase that was specified when the user was created. If none was specified but an encryption algorithm was given, the authentication passphrase will be used. This is required when the -x parameter is given or whenever a user’s privilege specification has a priv after it, requiring encryption.

      Using this information, you can construct your commands. Given how you set up your bootstrap user, the commands you will be using with that account will look like this:

      snmp_command -u bootstrap -l authPriv -a MD5 -x DES -A temp_password -X temp_password remote_host snmp_sub_command_or_options
      

      From your manager server, test to make sure your bootstrap account is available. Type the following to display the system information for the agent server:

      • snmpget -u bootstrap -l authPriv -a MD5 -x DES -A temp_password -X temp_password agent_server_ip_address 1.3.6.1.2.1.1.1.0

      The 1.3.6.1.2.1.1.1.0 string is the OID that is responsible for displaying system information. It will return the output of uname -a on the remote system.

      This will give the following output:

      Output

      SNMPv2-MIB::sysDescr.0 = STRING: Linux agent 4.15.0-66-generic #75-Ubuntu SMP Tue Oct 1 05:24:09 UTC 2019 x86_64

      Now that you have verified that you can authenticate to the server running the SNMP daemon, you can continue on to create your regular user account.

      Step 5 — Setting Up the Regular User Account

      Although you have specified the privileges for the demo user account in the snmpd.conf file, you haven’t actually created this user yet. In this step, you are going to use the bootstrap user as a template for your new user. You will do this using the snmpusm tool, which is used for user management.

      On the manager server, you can create the user from the template using the snmpusm tool and the following general syntax:

      snmpusm authentication_info agent_server_ip_address create new_user existing_user
      

      Using what you know about the authentication flags you need to pass, and leveraging the user account you already have (bootstrap), you can make a user that fits the user privileges you have already defined (demo).

      The command will look like this:

      • snmpusm -u bootstrap -l authPriv -a MD5 -x DES -A temp_password -X temp_password agent_server_ip_address create demo bootstrap

      You will receive the following message:

      Output

      User successfully created.

      You now have a fully functioning user called demo on your agent server. However, it is still using the same authentication information as the bootstrap account. To increase security, you can change the password to something else. This time, you will use the demo account to authenticate. Remember, passwords must be at least eight characters long:

      • snmpusm -u demo -l authPriv -a MD5 -x DES -A temp_password -X temp_password agent_server_ip_address passwd temp_password new_password

      You will receive the following message back:

      Output

      SNMPv3 Key(s) successfully changed.

      You can test your new credentials and password by asking the agent server how long the SNMP service has been running. You will use the snmpget command to get a single value from the agent server.

      This time, take advantage of the extra MIB definitions you downloaded to ask for the value by name instead of the OID numeric ID.

      • snmpget -u demo -l authPriv -a MD5 -x DES -A new_password -X new_password agent_server_ip_address sysUpTime.0

      You will get back a value that represents the last time that the remote SNMP daemon was restarted:

      Output

      DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (53309) 0:08:53.09

      You now have a working user account named demo. In the next step, you will simplify working with SNMP commands by configuring the client.

      Step 6 — Creating a Client Configuration File

      You have probably noticed by this point that the authentication details for all of your SNMP commands will be fairly static with each request. Rather than typing these in each time, you can create a client-side configuration file that will contain the credentials you are connecting with.

      The client configuration file can be placed in two different locations depending on how wide-spread you wish to share it.

      If you want to share your login credentials with any valid user on your management machine, you can place your configuration details into the global snmp.conf file on the manager server. You would need to open that file with sudo privileges:

      • sudo nano /etc/snmp/snmp.conf

      If, however, you want to define the authentication credentials for your user alone, you can create a hidden .snmp directory within your user’s home directory on the manager server, and create the file there:

      • mkdir ~/.snmp
      • nano ~/.snmp/snmp.conf

      Regardless of your decision on where to place your configuration, the contents will be the same.

      The commands that you will be using to authenticate are in the following table. In the right-hand column, you can see the directive names used to set those configuration details within the snmp.conf file:

      Command Flag Description Translated snmp.conf directive
      -u username The SNMPv3 username to authenticate as. defSecurityName username
      -l authPriv The security level to authenticate with. defSecurityLevel authPriv
      -a MD5 The authentication protocol to use. defAuthType MD5
      -x DES The privacy (encryption) protocol to use. defPrivType DES
      -A passphrase The authentication passphrase for the supplied username. defAuthPassphrase passphrase
      -X passphrase The privacy passphrase from the supplied username. defPrivPassphrase passphrase

      Using this information, you can construct an appropriate snmp.conf file. For this guide, it will look like this:

      snmp.conf

      defSecurityName demo
      defSecurityLevel authPriv
      defAuthType MD5
      defPrivType DES
      defAuthPassphrase new_password
      defPrivPassphrase new_password
      

      When you are finished, save and close the file.

      Now, you can issue commands without supplying the authentication details. You will only need the SNMP command, the host, and the command arguments.

      Instead of typing:

      • snmpget -u demo -l authPriv -a MD5 -x DES -A new_password -X new_password agent_server_ip_address sysUpTime.0

      You can type:

      • snmpget agent_server_ip_address sysUpTime.0

      As you can see, this significantly reduces the amount of information you need to supply in each request. Next, you will remove the bootstrap account to tighten the network security.

      Step 7 — Removing the Bootstrap Account

      Now that your regular account is configured correctly, you can remove the insecure bootstrap account.

      On your agent server, open the /etc/snmp/snmpd.conf file again with sudo privileges.

      • sudo nano /etc/snmp/snmpd.conf

      Find and comment out (or remove) both of the lines that you previously added that reference the bootstrap user:

      /etc/snmp/snmpd.conf

      ...
      #createUser bootstrap MD5 temp_password DES
      #rwuser bootstrap priv
      ...
      

      Save and close the file.

      Now, restart the SNMP daemon:

      • sudo systemctl restart snmpd

      This will fulfill the recommendation of not having createUser directives in the normal snmpd.conf file. It will also remove privileges from that temporary user.

      If you want to completely remove the bootstrap user from the usmUserTable, you can do so by issuing this command from the manager server:

      • snmpusm agent_server_ip_address delete bootstrap

      You will receive the following response:

      Output

      User successfully deleted.

      Conclusion

      At this point, you have a fully configured client-server setup that can communicate securely using the SNMP protocol. You can now add additional daemons on other hosts and configure account access across your entire infrastructure.

      For further study, you can use our How To Use the Net-SNMP Tool Suite To Manage and Monitor Servers tutorial to learn about SNMP tools and how to use them to retrieve values one-by-one or by bulk and how to modify data.



      Source link

      Create a TCP and UDP Client and Server using Go


      Updated by Linode Contributed by Mihalis Tsoukalos

      Go is a compiled, statically typed programming language developed by Google. Many modern applications, including Docker, Kubernetes, and Terraform, are written in Go. Go packages allow developers to organize and reuse Go code in a simple and maintainable manner.

      In this guide, you will use the net package, which is a part of Go’s standard library, to create TCP and UDP servers and clients. This guide is meant to provide instructional examples to help you become more familiar with the Go programming language.

      Scope of this Guide

      Throughout this guide you will create the following:

      • A TCP server and client. The TCP server accepts incoming messages from a TCP client and responds with the current date and time.
      • A UDP server and client. The UDP server accepts incoming messages from a UDP client and responds with a random number.
      • A concurrent TCP server that accepts incoming messages from several TCP clients and responds with the number of clients currently connected to it.

      Before You Begin

      1. If you are not familiar with using Go packages, review the Getting Started with Go Packages guide.

      2. Install Go on your computer if it is not already installed. You can follow our guide How to Install Go on Ubuntu for installation steps.

        This guide requires Go version 1.8 or higher. It is considered good practice to have the latest version of Go installed. You can check your Go version by executing the following command:

        go version
        

      Note

      This guide is written for a non-root user. Depending on the TCP/IP port number that you use when running the TCP and UDP servers, you may need to prefix commands with sudo. If you are not familiar with the sudo command, see the Users and Groups guide.

      Protocol Definitions

      Protocol Definition
      TCP (Transmission Control Protocol) TCP’s principal characteristic is that it is a reliable protocol by design. If there is no proof of a packet’s delivery, TCP will resend the packet. Some of the tasks TCP packets can be used for are establishing connections, transferring data, sending acknowledgements, and closing connections.
      IP (Internet Protocol) The IP protocol adheres to the end-to-end principle, which places all network intelligence in the end nodes and not in the intermediary nodes. This design favors a reduction in network complexity over reliability. For this reason, the Internet Protocol does not guarantee a reliable delivery of packets over a network. Instead, IP works together with TCP to reliably deliver packets over a network.
      UDP (User Datagram Protocol): UDP provides a simpler implementation of the transport layer protocol that, while less reliable than TCP, is much faster. UDP does not provide error checking, correction or packet retransmission, which makes it very fast. When speed is more important than reliability, UDP is generally chosen over TCP. UDP is commonly used for online gaming, video chatting, and other real-time applications.

      The net Package

      Go’s net package provides a portable interface for network I/O, including TCP/IP, UDP, domain name resolution, and Unix domain sockets. You will use this package to create TCP and UDP servers and clients in this guide.

      net Package Functions

      Use the table below as a quick reference for some of the net package functions used throughout this guide. To view all types and functions included in the net package, see Golang’s official documentation.

      Note

      All versions of net.Dial() and net.Listen() return data types that implement the io.Reader and io.Writer interfaces. This means that you can use regular File I/O functions to send and receive data from a TCP/IP connections.
      Type Function
      type Listener func Listen(network, address string) (Listener, error)

         • The network parameter defines the type of network to use and accepts values tcp, tcp4 (IPv4-only), tcp6 (IPv6-only), unix (Unix sockets), or unixpacket.

         • The address parameter defines the server address and port number that the server will listen on.

      type UDPConn func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)

         • Used to create UDP servers.

         • The network parameter must be a UDP network name.

         • The laddr parameter defines the server address and port number that the server will listen on.

      func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)

         • Used to specify the kind of client you will create.

         • The network parameter must be a UDP network name.

         • The laddr is the listening address (server). If laddr is nil, a local address is automatically chosen.

         • raddr is the response address (client). If the IP field of raddr is nil or an unspecified IP address, the local system is assumed.

      type UDPAddr func ResolveUDPAddr(network, address string) (*UDPAddr, error)

         • This function returns the address of a UDP end point.

         • The network parameter must be a UDP network name.

         • The address parameter has the form host:port. The host must be a an IP address, or a host name that can be resolved to IP addresses.

      type TCPAddr func ResolveTCPAddr(network, address string) (*TCPAddr, error)

         • This function returns the address of a TCP end point.

         • The network parameter must be a TCP network name.

         • The address parameter has the form host:port. The host must be a an IP address, or a host name that can be resolved to IP addresses.

      type Conn func Dial(network, address string) (Conn, error)

         • This function connects to the address on the named network.

         • The network parameter can be tcp, tcp4 (IPv4-only), tcp6 (IPv6-only), udp, udp4 (IPv4-only), udp6 (IPv6-only), ip, ip4 (IPv4-only), ip6 (IPv6-only), unix, unixgram and unixpacket.

         • When using TCP or UDP networks, the address parameter has the form host:port. The host must be a an IP address, or a host name that can be resolved to IP addresses.

      type TCPConn func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)

         • This function connects to the address on the TCP networks.

         • The network parameter must be a TCP network name.

         • The laddr is the listening address (server). If laddr is nil, a local address is automatically chosen.

         • raddr is the response address (client). If the IP field of raddr is nil or an unspecified IP address, the local system is assumed.

      Create a TCP Client and Server

      In this section, you will create a generic TCP client and server using Go. After creating the client and server, you will run them to test their connection with each other.

      Note

      Create the TCP Client

      The TCP client that you will create in this section will allow you to interact with any TCP server.

      1. In your current working directory, create a file named tcpC.go with the following content:

        ./tcpC.go
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        
        package main
        
        import (
                "bufio"
                "fmt"
                "net"
                "os"
                "strings"
        )
        
        func main() {
                arguments := os.Args
                if len(arguments) == 1 {
                        fmt.Println("Please provide host:port.")
                        return
                }
        
                CONNECT := arguments[1]
                c, err := net.Dial("tcp", CONNECT)
                if err != nil {
                        fmt.Println(err)
                        return
                }
        
                for {
                        reader := bufio.NewReader(os.Stdin)
                        fmt.Print(">> ")
                        text, _ := reader.ReadString('n')
                        fmt.Fprintf(c, text+"n")
        
                        message, _ := bufio.NewReader(c).ReadString('n')
                        fmt.Print("->: " + message)
                        if strings.TrimSpace(string(text)) == "STOP" {
                                fmt.Println("TCP client exiting...")
                                return
                        }
                }
        }
            
        • This file creates the main package, which declares the main() function. The function will use the imported packages to create a TCP client.
        • The main() function gathers command line arguments in the arguments variable and makes sure that a value for host:port was sent.
        • The CONNECT variable stores the value of arguments[1]to be used in the net.Dial() call.
        • A call to net.Dial() begins the implementation of the TCP client and will connect you to the desired TCP server. The second parameter of net.Dial() has two parts; the first is the hostname or the IP address of the TCP server and the second is the port number the TCP server listens on.
        • bufio.NewReader(os.Stdin) and ReadString() is used to read user input. Any user input is sent to the TCP server over the network using Fprintf().
        • bufio reader and the bufio.NewReader(c).ReadString('n') statement read the TCP server’s response. The error variable is ignored here for simplicity.
        • The entire for loop that is used to read user input will only terminate when you send the STOP command to the TCP server.

      Create the TCP Server

      You are now ready to create the TCP server. The TCP server will return the current date and time to the TCP client using a single network packet.

      1. In your current working directory, create a file named tcpS.go with the following content:

        ./tcpS.go
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        
        package main
        
        import (
                "bufio"
                "fmt"
                "net"
                "os"
                "strings"
                "time"
        )
        
        func main() {
                arguments := os.Args
                if len(arguments) == 1 {
                        fmt.Println("Please provide port number")
                        return
                }
        
                PORT := ":" + arguments[1]
                l, err := net.Listen("tcp", PORT)
                if err != nil {
                        fmt.Println(err)
                        return
                }
                defer l.Close()
        
                c, err := l.Accept()
                if err != nil {
                        fmt.Println(err)
                        return
                }
        
                for {
                        netData, err := bufio.NewReader(c).ReadString('n')
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                        if strings.TrimSpace(string(netData)) == "STOP" {
                                fmt.Println("Exiting TCP server!")
                                return
                        }
        
                        fmt.Print("-> ", string(netData))
                        t := time.Now()
                        myTime := t.Format(time.RFC3339) + "n"
                        c.Write([]byte(myTime))
                }
        }
            
        • This file creates the main package, which declares the main() function. The function will use the imported packages to create a TCP server.
        • The main() function gathers command line arguments in the arguments variable and includes error handling.
        • The net.Listen() function makes the program a TCP server. This functions returns a Listener variable, which is a generic network listener for stream-oriented protocols.
        • It is only after a successful call to Accept() that the TCP server can begin to interact with TCP clients.
        • The current implementation of the TCP server can only serve the first TCP client that connects to it, because the Accept() call is outside of the for loop. In the Create a Concurrent TCP Server section of this guide, you will see a TCP server implementation that can serve multiple TCP clients using Goroutines.
        • The TCP server uses regular File I/O functions to interact with TCP clients. This interaction takes place inside the for loop. Similarly to the TCP client, when the TCP server receives the STOP command from the TCP client, it will terminate.

      Test the TCP Client and Server

      You can now test your TCP client and server. You will need to execute the TCP server first so that the TCP client has somewhere it can connect to.

      1. Run your TCP server. From the directory containing the tcpS.go file, run the following command:

        go run tcpS.go 1234
        

        The server will listen on port number 1234. You will not see any output as a result of this command.

      2. Open a second shell session to execute the TCP client and to interact with the TCP server. Run the following command:

        go run tcpC.go 127.0.0.1:1234
        

        Note

        If the TCP server is not running on the expected TCP port, you will get the following error message from tcpC.go:

        dial tcp [::1]:1234: connect: connection refused
        
      3. You will see a >> prompt waiting for you to enter some text. Type in Hello! to receive a response from the TCP server:

        Hello!
        

        You should see a similar output:

          
        >> Hello!
        ->: 2019-05-23T19:43:21+03:00
            
        
      4. Send the STOP command to exit the TCP client and server:

        STOP
        

        You should see a similar output in the client:

          
        >> STOP
        ->: TCP client exiting...
            
        

        The output on the TCP server side will resemble the following:

          
        -> Hello!
        Exiting TCP server!
            
        

      Note

      The TCP server waits before writing back to the TCP client, whereas the client writes to the TCP server first and then waits to receive an answer. This behavior is part of the protocol definition that governs a TCP or a UDP connection. In this example, you have implemented an unofficial protocol that is based on TCP.

      Create a UDP Client and Server

      In this section, you will create a UDP client and server. After creating the client and server, you will run them both to test their connection with each other. A UDP client can be generic and can communicate with multiple UDP servers. On the other hand, a UDP server cannot be completely generic, because it typically implements a specific functionality. In the case of our UDP server example, it will return random numbers to UDP clients that connect to it.

      Create the UDP Client

      The UDP client that you will create in this section will allow you to interact with any UDP server.

      1. In your current working directory, create a file named udpC.go with the following content:

        ./udpC.go
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        
        package main
        
        import (
                "bufio"
                "fmt"
                "net"
                "os"
                "strings"
        )
        
        func main() {
                arguments := os.Args
                if len(arguments) == 1 {
                        fmt.Println("Please provide a host:port string")
                        return
                }
                CONNECT := arguments[1]
        
                s, err := net.ResolveUDPAddr("udp4", CONNECT)
                c, err := net.DialUDP("udp4", nil, s)
                if err != nil {
                        fmt.Println(err)
                        return
                }
        
                fmt.Printf("The UDP server is %sn", c.RemoteAddr().String())
                defer c.Close()
        
                for {
                        reader := bufio.NewReader(os.Stdin)
                        fmt.Print(">> ")
                        text, _ := reader.ReadString('n')
                        data := []byte(text + "n")
                        _, err = c.Write(data)
                        if strings.TrimSpace(string(data)) == "STOP" {
                                fmt.Println("Exiting UDP client!")
                                return
                        }
        
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
        
                        buffer := make([]byte, 1024)
                        n, _, err := c.ReadFromUDP(buffer)
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                        fmt.Printf("Reply: %sn", string(buffer[0:n]))
                }
        }
              
        • This file creates the main package, which declares the main() function. The function will use the imported packages to create a UDP client.
        • The main() function gathers command line arguments in the arguments variable and includes error handling.
        • Regular File I/O functions are used by the UDP client to interact with the UDP server. The client will terminate when you send the STOP command to the UDP server. This is not part of the UDP protocol, but is used in the example to provide the client with a way to exit.
        • A UDP end point address is returned by the net.ResolveUDPAddr() function. The UDP end point is of type UDPAddr and contains IP and port information.
        • The connection to the UDP server is established with the use of the net.DialUDP() function.
        • bufio.NewReader(os.Stdin) and ReadString() is used to read user input.
        • The ReadFromUDP() function reads a packet from the server connection and will return if it encounters an error.

      Create the UDP Server

      You are now ready to create the UDP server. You will write the UDP server code to respond to any connected client with random numbers.

      1. In your current working directory, create a file named udps.go with the following content:

        ./udpS.go
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        
        package main
        
        import (
                "fmt"
                "math/rand"
                "net"
                "os"
                "strconv"
                "strings"
                "time"
        )
        
        func random(min, max int) int {
                return rand.Intn(max-min) + min
        }
        
        func main() {
                arguments := os.Args
                if len(arguments) == 1 {
                        fmt.Println("Please provide a port number!")
                        return
                }
                PORT := ":" + arguments[1]
        
                s, err := net.ResolveUDPAddr("udp4", PORT)
                if err != nil {
                        fmt.Println(err)
                        return
                }
        
                connection, err := net.ListenUDP("udp4", s)
                if err != nil {
                        fmt.Println(err)
                        return
                }
        
                defer connection.Close()
                buffer := make([]byte, 1024)
                rand.Seed(time.Now().Unix())
        
                for {
                        n, addr, err := connection.ReadFromUDP(buffer)
                        fmt.Print("-> ", string(buffer[0:n-1]))
        
                        if strings.TrimSpace(string(buffer[0:n])) == "STOP" {
                                fmt.Println("Exiting UDP server!")
                                return
                        }
        
                        data := []byte(strconv.Itoa(random(1, 1001)))
                        fmt.Printf("data: %sn", string(data))
                        _, err = connection.WriteToUDP(data, addr)
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                }
        }
            
        • This file creates the main package, which declares the main() function. The function will use the imported packages to create a UDP server.
        • The main() function gathers command line arguments in the arguments variable and includes error handling.
        • The net.ListenUDP() function tells the application to listen for incoming UDP connections, which are served inside the for loop. This is the function call that makes the program a UDP server.
        • The ReadFromUDP() and WriteToUDP() functions are used to read data from a UDP connection and write data to a UDP connection, respectively. A byte slice is stored in the data variable and used to write the desired data. The buffer variable also stores a byte slice and is used to read data.
        • Since UDP is a stateless protocol, each UDP client is served and then the connection closes automatically. The UDP server program will only exit when it receives the STOP keyword from a UDP client. Otherwise, the server program will continue to wait for more UDP connections from other clients.

      Test the UDP Client and Server

      You can now test your UDP client and server. You will need to execute the UDP server first so that the UDP client has somewhere it can connect to.

      1. Run your UDP server. From the directory containing the udpS.go file, run the following command:

        go run udpS.go 1234
        

        The server will listen on port number 1234. You will not see any output as a result of this command.

      2. Open a second shell session to execute the UDP client and to interact with the UDP server. Run the following command:

        go run udpC.go 127.0.0.1:1234
        
      3. You will see a >> prompt waiting for you to enter some text. Type in Hello! to receive a response from the UDP server:

        Hello!
        

        You should see a similar output:

          
        The UDP server is 127.0.0.1:1234
        >> Hello!
        Reply: 82
            
        
      4. Send the STOP command to exit the UDP client and server:

        You should see a similar output on the client side:

          
        >> STOP
        Exiting UDP client!
            
        

        The output on the UDP server side will be as follows:

          
        -> STOP
        Exiting UDP server!
            
        

      Create a Concurrent TCP Server

      This section demonstrates the implementation of a concurrent TCP server. The benefit of a concurrent TCP server is that it can serve multiple clients. In Go, this is accomplished by creating a separate Goroutine to serve each TCP client.

      The example TCP server keeps a running count of the number of TCP clients it has served so far. The counter increases by one each time a new TCP client connects to the TCP server. The current value of that counter is returned to each TCP client.

      1. In your current working directory, create a file named concTCP.go with the following content:

        ./concTCP.go
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        
        package main
        
        import (
                "bufio"
                "fmt"
                "net"
                "os"
                "strconv"
                "strings"
        )
        
        var count = 0
        
        func handleConnection(c net.Conn) {
                fmt.Print(".")
                for {
                        netData, err := bufio.NewReader(c).ReadString('n')
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
        
                        temp := strings.TrimSpace(string(netData))
                        if temp == "STOP" {
                                break
                        }
                        fmt.Println(temp)
                        counter := strconv.Itoa(count) + "n"
                        c.Write([]byte(string(counter)))
                }
                c.Close()
        }
        
        func main() {
                arguments := os.Args
                if len(arguments) == 1 {
                        fmt.Println("Please provide a port number!")
                        return
                }
        
                PORT := ":" + arguments[1]
                l, err := net.Listen("tcp4", PORT)
                if err != nil {
                        fmt.Println(err)
                        return
                }
                defer l.Close()
        
                for {
                        c, err := l.Accept()
                        if err != nil {
                                fmt.Println(err)
                                return
                        }
                        go handleConnection(c)
                        count++
                }
        }
              
        • This file creates the main package, which declares the handleConnection() and main() functions.
        • The main() function will use the imported packages to create a concurrent TCP server. It gathers command line arguments in the arguments variable and includes error handling.
        • Each TCP client is served by a separate Goroutine that executes the handleConnection() function. This means that while a TCP client is served, the TCP server is free to interact with more TCP clients. TCP clients are connected using the Accept() function.
        • Although the Accept() function can be executed multiple times, the net.Listen() function needs to be executed only once. For this reason the net.Listen() function remains outside of the for loop.
        • The for loop in the main() function is endless because TCP/IP servers usually run nonstop. However, if the handleConnection() function receives the STOP message, the Goroutine that runs it will exit and the related TCP connection will close.

      Test the Concurrent TCP Server

      In this section, you will test the concurrent TCP server using the netcat command line utility.

      1. Run your concurrent TCP server. From the directory containing the concTCP.go file, run the following command:

        go run concTCP.go 1234
        

        The command creates a TCP server that listens on port number 1234. You can use any port number, however, ensure it is not already in use and that you have the required privileges. Reference the list of well-known TCP and UDP ports, if needed.

      2. Use netcat to establish a connection with the TCP server. By default, netcat will establish a TCP connection with a remote host on the specified port number.

        nc 127.0.0.1 1234
        
      3. After issuing the previous command, you will not see any change in your output. Type Hello! to send a packet to the TCP server:

        Hello!
        

        The TCP server will return the number of current client connections as its response. Since this is your first connection established with the TCP server, you should expect an output of 1.

          
        Hello!
        1
            
        

        If you’d like, you can open a new shell session and use netcat to establish a second connection with the TCP server by repeating Step 2. When you send the server a second Hello!, you should receive a response of 2 this time.

      4. You can also connect to the TCP server using the TCP client you created in the Create the TCP Client section of the guide. Ensure you are in the directory containing the tcpC.go file and issue the following command:

        go run tcpC.go 127.0.0.1:1234
        
      5. You will see a >> prompt waiting for you to enter some text. Type in Hello! to receive a response from the TCP server:

        Hello!
        

        You should see a similar output indicating 3 client connections:

          
        >> Hello!
        ->: 3
            
        
      6. Send the STOP command to exit the TCP client:

        You should see a similar output on the client:

          
        >> STOP
        ->: TCP client exiting...
              
        

        The output on the TCP server side will be as follows:

          
        .Hello!
        .Hello!
        .Hello!
              
        

        Note

        From the shell session running the TCP server, type CTRL-c to interrupt program execution and then, CTRL-D to close all client connections and to stop the TCP server.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link