Sauvegarde Time Machine sur SmartOS

J’avais déjà mis en place une zone avec netatalk pour enregistrer mes sauvegardes time machine sur un dataset zfs à travers le réseau. Sans rentrer dans le détail, après avoir créé le dataset, il faut créer une nouvelle zone native avec un manifest qui ressemble à ceci :

{
 "brand": "joyent",
 "image_uuid": "e75c9d82-3156-11ea-9220-c7a6bb9f41b6",
 "alias": "tm",
 "hostname": "tm",
 "max_physical_memory": 128,
 "quota": 10,
 "dns_domain": "chez.moi",
 "resolvers": ["192.168.1.1"],
 "nics": [
  {
    "nic_tag": "admin",
    "ip": "192.168.1.100",
    "netmask": "255.255.255.0",
    "gateway": "192.168.1.1"
  }
 ],
        "filesystems":
        [
                {
                        "options": "rw",
                        "type": "lofs",
                        "source": "/zones/tm",
                        "target": "/tm"
                }
        ],
"customer_metadata": {
    "root_authorized_keys": "ssh-rsa ma-clé-publique",
    "user-script" : "/usr/sbin/mdata-get root_authorized_keys > ~root/.ssh/authorized_keys ; /usr/sbin/mdata-get root_authorized_keys > ~admin/.ssh/authorized_keys"
}
}

Il faut installer le paquet netatalk :

pkgin in netatalk3

Editer le fichier de configuration /opt/local/etc/netatalk/afp.conf :

[Global]
log file = /var/log/netatalk.log
uam list = uams_dhx.so,uams_dhx2.so
afp listen = 0.0.0.0

[tm]
path = /tm
valid users = moi
rwlist = moi
time machine = yes

Ceci permet donc de partager le répertoire /tm qu’on importé depuis la zone globale à travers netatalk pour l’utilisateur (local à la zone) moi, l’authentification passera par pam, il ne faut pas oublier de créer l’utilisateur et lui affecter un mot de passe.

Pour que le service démarre en même temps que la zone, il faut importer le manifest livré dans le paquet :

svccfg import /opt/local/lib/svc/manifest/netatalk3.xml

Je n’ai pas réussi à le faire avec la version du paquet que j’avais, j’ai donc exporté la définition de la version précédente

# svccfg export svc:/pkgsrc/netatalk:default
<?xml version='1.0'?>                                         
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>                                                                                 
<service_bundle type='manifest' name='export'>
  <service name='pkgsrc/netatalk' type='service' version='0'>
    <create_default_instance enabled='true'/>
    <single_instance/>
    <dependency name='network' grouping='require_all' restart_on='error' type='service'>
      <service_fmri value='svc:/milestone/network:default'/>
    </dependency>                     
    <dependency name='filesystem' grouping='require_all' restart_on='error' type='service'>
      <service_fmri value='svc:/system/filesystem/local'/>
    </dependency>         
    <dependency name='mdns' grouping='optional_all' restart_on='error' type='service'>
      <service_fmri value='svc:/network/dns/multicast'/>
    </dependency>
    <method_context/>
    <exec_method name='start' type='method' exec='/opt/local/libexec/netatalk/netatalk' timeout_seconds='60'/>
    <exec_method name='stop' type='method' exec=':kill' timeout_seconds='60'/>
    <property_group name='application' type='application'>
      <propval name='config_file' type='astring' value='/opt/local/etc/netatalk/afp.conf'/>
    </property_group>                                                                                                                                                                                                                                                                                                          
    <property_group name='startd' type='framework'>                      
      <propval name='duration' type='astring' value='contract'/>
      <propval name='ignore_error' type='astring' value='core,signal'/>
    </property_group>             
    <stability value='Evolving'/>  
    <template>                    
      <common_name>                 
        <loctext xml:lang='C'>Netatalk AFP Server</loctext>
      </common_name>                                               
    </template>
  </service>                                                                                                                                                                                                                                                                                                                   
</service_bundle>

On peut activer le service :

svcadm enable -r svc:/pkgsrc/netatalk:default

Pour qu’il soit visible depuis le réseau local à travers le dns multicast, il faut activer le service mdns, netatalk est compilé pour s’enregistrer auprès du serveur :

svcadm enable svc:/network/dns/multicast:default

Avec l’avènement de APFS, le protocole afp devient obsolète et ne permet plus de partager les volumes formatés pour utiliser ce système de fichiers dans macOS. Les sauvegardes time machine sont faites dans une image de type sparse image formatée en HFS+, ça ne pose donc pas de problème tant qu’Apple ne décide pas de passer à APFS. SmartOS permet le partage par SMB à travers les fonctionnalités CIFS du noyau mais seul samba 4 dispose des fonctionnalités spécifiques à la sauvegarde time machine. Il faut les options suivantes dans la configuration globale :

fruit:advertise_fullsync = true
fruit:resource = xattr

On peut ajouter un partage, notez les options spécifiques à time machine :

[tm]
   comment = Time Machine
   browseable = yes
   writable = yes
   guest ok = no
   vfs objects = catia fruit streams_xattr
   fruit:aapl = yes
   fruit:time machine = yes
   path = /tm

Comme dans le cas précédent, pour que le partage soit vu depuis le réseau local, il faut activer le serveur mdns et ajouter les entrées correspondantes. Il semble que le code qui permet de le faire dans samba n’existe pas et il y a même une proposition de suppression de l’API ; ce dernier ne le propose que pour l’implémentation avahi. On pourrait simplement installer le paquet correspondant mais autant simplement ajouter les entrées correspondantes à notre service avec la commande dns-sd. La discussion a déjà eu lieu, voici donc les valeurs que j’ai utilisées :

dns-sd -R tm "_smb._tcp." "local" "445"&
dns-sd -R tm "_device-info._tcp." "local" "0" "model=TimeCapsule"&
dns-sd -R tm "_adisk._tcp." "local" "9" "sys=waMA=0,adVF=0x100" "dk0=adVN=tm,adVF=0x82"&

J’ai ajouté ces commandes dans /opt/custom/bin/postboot comme décrit dans ce gist, pour qu’elles soient exécutées au démarrage de la zone.