diff --git a/CHANGELOG.md b/CHANGELOG.md index 62224e9..30bdd68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ The release notes on GitHub are taken from this file: the section whose heading is the version number. +## 1.6.0 + +Sign-in and users +- A new installation and the demo sign in as admin / traffic66; the + sign-in page shows it while it is still in use. A new installation asks + for a new password at the first sign-in, before anything else. + Installations that already have a password file keep it. +- **Account** at the foot of the menu: change your password. The + administrator (admin) also adds and deletes users and resets passwords; + the password file is the same one `traffic66 passwd` writes. +- LDAP / Active Directory sign-in is shown as in development. +- With -password or TRAFFIC66_PASSWORD, passwords are not changed from the + web UI, and the page says so. + ## 1.5.2 Fixes diff --git a/README.md b/README.md index 3ba9ce1..744a1ce 100644 --- a/README.md +++ b/README.md @@ -43,12 +43,12 @@ Download your system's archive from the (Windows x64, Linux x86-64/ARM64 with kernel 3.2+, macOS 11+), unpack it and run: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` On macOS first run `xattr -dr com.apple.quarantine `. Open -http://127.0.0.1:8066 as `admin` / `try66`: a day of history and live +http://127.0.0.1:8066 as `admin` / `traffic66`: a day of history and live traffic from four simulated devices, including an attack shown step by step on **Findings**. Ctrl+C stops it; delete `traffic66-demo` to start afresh. To run it next to a real installation: `-addr :8067 -listen ""`. @@ -103,8 +103,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -Double-clicking `traffic66.exe` also works: it opens the web UI and shows -the first password in its window. +Double-clicking `traffic66.exe` also works: it opens the web UI. **macOS**: unpack to `/usr/local/traffic66`, remove the quarantine flag, run `traffic66 passwd -data "/Library/Application Support/traffic66"`, and @@ -113,11 +112,16 @@ start it from a LaunchDaemon whose `ProgramArguments` are the program, ## 3. Users and passwords -On first start traffic66 creates the user `admin` with a random password -and prints it once (in the window, the terminal, or -`journalctl -u traffic66 | grep "first start"`). Users are kept as salted -hashes in `password` in the data directory and managed with one command on -the traffic66 machine (add `-data …` when traffic66 runs with it): +A new installation signs in as `admin` / `traffic66`, and the first sign-in +asks for a new password before anything else is shown (the demo keeps +`traffic66`). Afterwards **Account**, at the foot of the menu, changes your +password; the administrator (`admin`) also adds and deletes users and +resets their passwords there. Sign-in with LDAP / Active Directory is in +development. + +Users are kept as salted hashes in `password` in the data directory. The +same can be done on the traffic66 machine with one command (add `-data …` +when traffic66 runs with it): | To | Command | |---|---| @@ -126,8 +130,8 @@ the traffic66 machine (add `-data …` when traffic66 runs with it): | Delete `alice` | `traffic66 passwd -user alice -delete` | | List users | `traffic66 passwd -list` | -Changes apply at once. All users have the same rights. For scripts and -containers, `TRAFFIC66_PASSWORD=…` (or `-password`) accepts only `-user` +Changes apply at once. Apart from managing users, all users have the same +rights. For scripts and containers, `TRAFFIC66_PASSWORD=…` (or `-password`) accepts only `-user` with that password for that run. Five wrong passwords in a minute block the address for a minute. diff --git a/cmd/traffic66/main.go b/cmd/traffic66/main.go index dab6fba..d32d57c 100644 --- a/cmd/traffic66/main.go +++ b/cmd/traffic66/main.go @@ -382,7 +382,7 @@ func serve(args []string, demo bool) { poller.Run(ctx) }() - checker := loginChecker(f.data, f.user, f.password) + checker := loginChecker(f.data, f.user, f.password, demo) tok := randomHex(24) tokPath := filepath.Join(f.data, ".tui-token") os.WriteFile(tokPath, []byte(tok), 0o600) @@ -394,7 +394,7 @@ func serve(args []string, demo bool) { dns = dnsres.New(dnsres.Options{Upstream: f.dnsUpstream, PerSecond: f.dnsRate, TTL: f.dnsTTL}) } srv := &api.Server{Store: st, Pipe: pipe, Col: col, Inv: inv, ASN: asn, Thr: thr, DNS: dns, Det: det, Static: web.FS(), Version: version, - Demo: demo, Check: checker.Check, Exists: checker.Exists, LocalTok: tok, DataDir: f.data, Started: time.Now()} + Demo: demo, Check: checker.Check, Exists: checker.Exists, Accounts: checker, LocalTok: tok, DataDir: f.data, Started: time.Now()} srv.SNMP = poller.Status licDir := f.data if offline != nil { @@ -688,8 +688,9 @@ func defaultDir(name string) string { // loginChecker decides where the password comes from: -password, then // TRAFFIC66_PASSWORD, then the password file in the data directory. On the -// very first start it creates the file with a generated password. -func loginChecker(dir, user, flagPw string) *auth.FileChecker { +// very first start it creates the file with the default password, which +// the web UI asks to change at the first sign-in (except in the demo). +func loginChecker(dir, user, flagPw string, demo bool) *auth.FileChecker { pw := flagPw if pw == "" { pw = os.Getenv("TRAFFIC66_PASSWORD") @@ -704,12 +705,14 @@ func loginChecker(dir, user, flagPw string) *auth.FileChecker { fatalf("%s: %v", file, err) } if len(es) == 0 { - pw = auth.Generate() - if err := auth.Set(dir, user, pw); err != nil { + if err := auth.Set(dir, user, auth.DefaultPassword); err != nil { fatalf("saving the password: %v", err) } - log.Printf("first start: sign in as user %q with password %q", user, pw) - log.Printf("this password is kept (hashed) in %s; change it with: traffic66 passwd -data %s", file, quoteArg(dir)) + if demo { + log.Printf("demo: sign in as user %q with password %q", user, auth.DefaultPassword) + } else { + log.Printf("first start: sign in as user %q with password %q; a new password is asked for at the first sign-in", user, auth.DefaultPassword) + } } else { var users []string for _, e := range es { diff --git a/docs/README.ar.md b/docs/README.ar.md index 63aaed9..79e42cd 100644 --- a/docs/README.ar.md +++ b/docs/README.ar.md @@ -50,12 +50,12 @@ Kafka أو قاعدة بيانات منفصلة. (Windows x64، وLinux x86-64/ARM64 بنواة 3.2+، وmacOS 11+)، وفكّ ضغطه وشغّل: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` على macOS شغّل أولًا `xattr -dr com.apple.quarantine `. افتح -http://127.0.0.1:8066 وسجّل الدخول بـ `admin` / `try66`: سجلّ يوم كامل وحركة +http://127.0.0.1:8066 وسجّل الدخول بـ `admin` / `traffic66`: سجلّ يوم كامل وحركة حية من أربعة أجهزة محاكاة، ومنها هجوم يُعرض خطوةً خطوة في **الاكتشافات**. يوقفه Ctrl+C؛ احذف `traffic66-demo` لتبدأ من جديد. لتشغيله بجوار تثبيت حقيقي: `-addr :8067 -listen ""`. @@ -113,8 +113,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -النقر المزدوج على `traffic66.exe` يعمل أيضًا: يفتح واجهة الويب ويعرض كلمة -المرور الأولى في نافذته. +النقر المزدوج على `traffic66.exe` يعمل أيضًا: يفتح واجهة الويب. **macOS**: فكّ الضغط إلى `/usr/local/traffic66`، وأزل علامة الحجر، وشغّل `traffic66 passwd -data "/Library/Application Support/traffic66"`، ثم @@ -125,11 +124,15 @@ Start-ScheduledTask -TaskName traffic66 ## 3. المستخدمون وكلمات المرور -عند التشغيل الأول ينشئ traffic66 المستخدم `admin` بكلمة مرور عشوائية ويطبعها -مرة واحدة (في النافذة، أو الطرفية، أو -`journalctl -u traffic66 | grep "first start"`). يُحفظ المستخدمون بصيغة -تجزئات مملّحة في `password` داخل دليل البيانات، ويُدارون بأمر واحد على جهاز -traffic66 (أضف `-data …` إن كان traffic66 يعمل به): +يُسجَّل الدخول في التثبيت الجديد بـ `admin` / `traffic66`، ويطلب أول تسجيل +دخول كلمة مرور جديدة قبل عرض أي شيء آخر (يحتفظ العرض التجريبي بـ `traffic66`). +بعد ذلك تُغيّر كلمة مرورك من **الحساب** أسفل القائمة؛ ومن هناك أيضًا يضيف +المسؤول (`admin`) المستخدمين ويحذفهم ويعيد تعيين كلمات مرورهم. تسجيل الدخول عبر +LDAP / Active Directory قيد التطوير. + +يُحفظ المستخدمون بصيغة تجزئات مملّحة في `password` داخل دليل البيانات. ويمكن +فعل الشيء نفسه بأمر واحد على جهاز traffic66 (أضف `-data …` إن كان traffic66 +يعمل به): | المطلوب | الأمر | |---|---| @@ -138,8 +141,8 @@ traffic66 (أضف `-data …` إن كان traffic66 يعمل به): | حذف `alice` | `traffic66 passwd -user alice -delete` | | عرض المستخدمين | `traffic66 passwd -list` | -تُطبَّق التغييرات فورًا. لجميع المستخدمين الصلاحيات نفسها. للسكربتات -والحاويات، يقبل `TRAFFIC66_PASSWORD=…` (أو `-password`) المستخدم `-user` فقط +تُطبَّق التغييرات فورًا. باستثناء إدارة المستخدمين، لجميع المستخدمين +الصلاحيات نفسها. للسكربتات والحاويات، يقبل `TRAFFIC66_PASSWORD=…` (أو `-password`) المستخدم `-user` فقط بكلمة المرور تلك طوال ذلك التشغيل. خمس كلمات مرور خاطئة خلال دقيقة تحظر العنوان لمدة دقيقة. diff --git a/docs/README.bn.md b/docs/README.bn.md index 148b2e7..1311a5d 100644 --- a/docs/README.bn.md +++ b/docs/README.bn.md @@ -49,12 +49,12 @@ Elasticsearch, Kafka বা আলাদা database ছাড়াই। unpack করে চালান: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` macOS-এ আগে `xattr -dr com.apple.quarantine ` চালান। -http://127.0.0.1:8066 খুলে `admin` / `try66` দিয়ে সাইন ইন করুন: এক দিনের +http://127.0.0.1:8066 খুলে `admin` / `traffic66` দিয়ে সাইন ইন করুন: এক দিনের ইতিহাস আর চারটি simulated ডিভাইস থেকে live ট্রাফিক, সাথে একটি আক্রমণ যা **সন্দেহজনক কার্যকলাপ**-এ ধাপে ধাপে দেখানো হয়। Ctrl+C দিয়ে বন্ধ করুন; নতুন করে শুরু করতে `traffic66-demo` মুছে দিন। আসল installation-এর পাশাপাশি চালাতে: @@ -113,8 +113,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -`traffic66.exe`-এ double-click করলেও চলে: এটি web UI খোলে এবং প্রথম -পাসওয়ার্ড তার window-তে দেখায়। +`traffic66.exe`-এ double-click করলেও চলে: এটি web UI খোলে। **macOS**: `/usr/local/traffic66`-এ unpack করুন, quarantine flag সরান, `traffic66 passwd -data "/Library/Application Support/traffic66"` চালান, এবং @@ -125,11 +124,15 @@ Start-ScheduledTask -TaskName traffic66 ## 3. ইউজার ও পাসওয়ার্ড -প্রথমবার চালু হলে traffic66 একটি random পাসওয়ার্ডসহ ইউজার `admin` তৈরি করে -এবং সেটি একবার দেখায় (window-তে, terminal-এ, অথবা -`journalctl -u traffic66 | grep "first start"`)। ইউজাররা data directory-র -`password`-এ salted hash হিসেবে থাকে এবং traffic66 মেশিনে একটি command দিয়ে -পরিচালিত হয় (traffic66 `-data …` দিয়ে চললে সেটিও যোগ করুন): +নতুন installation-এ `admin` / `traffic66` দিয়ে সাইন ইন হয়, এবং প্রথম সাইন ইন +অন্য কিছু দেখানোর আগে নতুন পাসওয়ার্ড চায় (demo `traffic66`-ই রাখে)। এরপর +menu-র নিচে **অ্যাকাউন্ট**-এ আপনার পাসওয়ার্ড বদলান; administrator (`admin`) +সেখানেই ইউজার যোগ ও মুছতে এবং তাদের পাসওয়ার্ড reset করতে পারেন। LDAP / Active +Directory দিয়ে সাইন ইন উন্নয়নাধীন। + +ইউজাররা data directory-র `password`-এ salted hash হিসেবে থাকে। একই কাজ +traffic66 মেশিনে একটি command দিয়েও করা যায় (traffic66 `-data …` দিয়ে চললে +সেটিও যোগ করুন): | কাজ | Command | |---|---| @@ -138,8 +141,8 @@ Start-ScheduledTask -TaskName traffic66 | `alice` মুছে ফেলা | `traffic66 passwd -user alice -delete` | | ইউজারদের তালিকা দেখা | `traffic66 passwd -list` | -পরিবর্তন সঙ্গে সঙ্গে কার্যকর হয়। সব ইউজারের অধিকার একই। script ও -container-এর জন্য `TRAFFIC66_PASSWORD=…` (বা `-password`) ওই run-এ শুধু ওই +পরিবর্তন সঙ্গে সঙ্গে কার্যকর হয়। ইউজার পরিচালনা ছাড়া সব ইউজারের অধিকার +একই। script ও container-এর জন্য `TRAFFIC66_PASSWORD=…` (বা `-password`) ওই run-এ শুধু ওই পাসওয়ার্ডসহ `-user`-কে গ্রহণ করে। এক মিনিটের মধ্যে পাঁচবার ভুল পাসওয়ার্ড দিলে সেই address এক মিনিটের জন্য block হয়। diff --git a/docs/README.es.md b/docs/README.es.md index be1e236..97b5759 100644 --- a/docs/README.es.md +++ b/docs/README.es.md @@ -50,12 +50,12 @@ Descargue el archivo para su sistema desde la (Windows x64, Linux x86-64/ARM64 con kernel 3.2+, macOS 11+), descomprímalo y ejecute: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` En macOS ejecute antes `xattr -dr com.apple.quarantine `. Abra -http://127.0.0.1:8066 como `admin` / `try66`: un día de historial y tráfico +http://127.0.0.1:8066 como `admin` / `traffic66`: un día de historial y tráfico en vivo de cuatro equipos simulados, incluido un ataque mostrado paso a paso en **Hallazgos**. Ctrl+C la detiene; borre `traffic66-demo` para empezar de cero. Para ejecutarla junto a una instalación real: `-addr :8067 -listen ""`. @@ -113,8 +113,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -También funciona hacer doble clic en `traffic66.exe`: abre la interfaz web -y muestra la primera contraseña en su ventana. +También funciona hacer doble clic en `traffic66.exe`: abre la interfaz web. **macOS**: descomprima en `/usr/local/traffic66`, quite la marca de cuarentena, ejecute `traffic66 passwd -data "/Library/Application Support/traffic66"` @@ -125,12 +124,16 @@ programa, `-data` y ese directorio, con `RunAtLoad` y `KeepAlive`. ## 3. Usuarios y contraseñas -En el primer arranque traffic66 crea el usuario `admin` con una contraseña -aleatoria y la muestra una sola vez (en la ventana, en el terminal o con -`journalctl -u traffic66 | grep "first start"`). Los usuarios se guardan como -hashes con sal en `password` dentro del directorio de datos y se gestionan -con un solo comando en la máquina de traffic66 (añada `-data …` si traffic66 -se ejecuta con esa opción): +Una instalación nueva se entra como `admin` / `traffic66`, y el primer +inicio de sesión pide una contraseña nueva antes de mostrar nada más (la +demo mantiene `traffic66`). Después, **Cuenta**, al pie del menú, cambia su +contraseña; el administrador (`admin`) también añade y elimina usuarios y +restablece sus contraseñas allí. El inicio de sesión con LDAP / Active +Directory está en desarrollo. + +Los usuarios se guardan como hashes con sal en `password` dentro del +directorio de datos. Lo mismo puede hacerse en la máquina de traffic66 con un +solo comando (añada `-data …` si traffic66 se ejecuta con esa opción): | Para | Comando | |---|---| @@ -139,8 +142,8 @@ se ejecuta con esa opción): | Eliminar el usuario `alice` | `traffic66 passwd -user alice -delete` | | Listar los usuarios | `traffic66 passwd -list` | -Los cambios se aplican al instante. Todos los usuarios tienen los mismos -permisos. Para scripts y contenedores, `TRAFFIC66_PASSWORD=…` (o `-password`) +Los cambios se aplican al instante. Salvo la gestión de usuarios, todos los +usuarios tienen los mismos permisos. Para scripts y contenedores, `TRAFFIC66_PASSWORD=…` (o `-password`) acepta en esa ejecución solo `-user` con esa contraseña. Cinco contraseñas erróneas en un minuto bloquean la dirección durante un minuto. diff --git a/docs/README.fr.md b/docs/README.fr.md index b59bcdd..50a7cc4 100644 --- a/docs/README.fr.md +++ b/docs/README.fr.md @@ -50,12 +50,12 @@ Téléchargez l'archive de votre système depuis la (Windows x64, Linux x86-64/ARM64 avec noyau 3.2+, macOS 11+), décompressez-la et lancez : ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` Sous macOS, lancez d'abord `xattr -dr com.apple.quarantine `. Ouvrez -http://127.0.0.1:8066 en tant que `admin` / `try66` : une journée +http://127.0.0.1:8066 en tant que `admin` / `traffic66` : une journée d'historique et du trafic en direct venant de quatre équipements simulés, dont une attaque montrée étape par étape dans **Détections**. Ctrl+C l'arrête ; supprimez `traffic66-demo` pour repartir de zéro. Pour la lancer @@ -116,7 +116,7 @@ Start-ScheduledTask -TaskName traffic66 ``` Double-cliquer sur `traffic66.exe` fonctionne aussi : il ouvre l'interface -web et affiche le premier mot de passe dans sa fenêtre. +web. **macOS** : décompressez dans `/usr/local/traffic66`, retirez l'attribut de quarantaine, lancez `traffic66 passwd -data "/Library/Application Support/traffic66"` @@ -127,12 +127,17 @@ programme, `-data` et ce répertoire, avec `RunAtLoad` et `KeepAlive`. ## 3. Utilisateurs et mots de passe -Au premier démarrage, traffic66 crée l'utilisateur `admin` avec un mot de -passe aléatoire et l'affiche une seule fois (dans la fenêtre, le terminal -ou `journalctl -u traffic66 | grep "first start"`). Les utilisateurs sont -enregistrés sous forme de hachages salés dans `password` dans le répertoire -de données et se gèrent avec une seule commande sur la machine traffic66 -(ajoutez `-data …` si traffic66 tourne avec cette option) : +Une nouvelle installation se connecte en tant que `admin` / `traffic66`, et +la première connexion demande un nouveau mot de passe avant d'afficher quoi +que ce soit d'autre (la démo garde `traffic66`). Ensuite, **Compte**, en bas +du menu, change votre mot de passe ; l'administrateur (`admin`) y ajoute et +supprime aussi des utilisateurs et réinitialise leurs mots de passe. La +connexion via LDAP / Active Directory est en développement. + +Les utilisateurs sont enregistrés sous forme de hachages salés dans +`password` dans le répertoire de données. La même chose se fait sur la +machine traffic66 avec une seule commande (ajoutez `-data …` si traffic66 +tourne avec cette option) : | Pour | Commande | |---|---| @@ -141,8 +146,8 @@ de données et se gèrent avec une seule commande sur la machine traffic66 | Supprimer l'utilisateur `alice` | `traffic66 passwd -user alice -delete` | | Lister les utilisateurs | `traffic66 passwd -list` | -Les changements s'appliquent immédiatement. Tous les utilisateurs ont les -mêmes droits. Pour les scripts et les conteneurs, `TRAFFIC66_PASSWORD=…` (ou +Les changements s'appliquent immédiatement. Hormis la gestion des +utilisateurs, tous les utilisateurs ont les mêmes droits. Pour les scripts et les conteneurs, `TRAFFIC66_PASSWORD=…` (ou `-password`) n'accepte que `-user` avec ce mot de passe pour cette exécution. Cinq mots de passe erronés en une minute bloquent l'adresse pendant une minute. diff --git a/docs/README.hi.md b/docs/README.hi.md index 231c07e..9155e68 100644 --- a/docs/README.hi.md +++ b/docs/README.hi.md @@ -49,12 +49,12 @@ Elasticsearch, Kafka या अलग database के। और चलाएँ: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` macOS पर पहले `xattr -dr com.apple.quarantine ` चलाएँ। -http://127.0.0.1:8066 खोलें और `admin` / `try66` से साइन इन करें: एक दिन का +http://127.0.0.1:8066 खोलें और `admin` / `traffic66` से साइन इन करें: एक दिन का इतिहास और चार simulated डिवाइसों से live ट्रैफ़िक, जिसमें एक हमला भी है जिसे **संदिग्ध गतिविधि** पर चरण-दर-चरण दिखाया जाता है। Ctrl+C से बंद करें; नए सिरे से शुरू करने के लिए `traffic66-demo` हटा दें। असली installation के साथ-साथ @@ -113,8 +113,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -`traffic66.exe` पर double-click करना भी काम करता है: यह web UI खोलता है और -पहला पासवर्ड अपनी window में दिखाता है। +`traffic66.exe` पर double-click करना भी काम करता है: यह web UI खोलता है। **macOS**: `/usr/local/traffic66` में unpack करें, quarantine flag हटाएँ, `traffic66 passwd -data "/Library/Application Support/traffic66"` चलाएँ, और @@ -125,12 +124,15 @@ Start-ScheduledTask -TaskName traffic66 ## 3. यूज़र और पासवर्ड -पहली बार शुरू होने पर traffic66 एक random पासवर्ड के साथ यूज़र `admin` बनाता -है और उसे एक बार दिखाता है (window में, terminal में, या -`journalctl -u traffic66 | grep "first start"` में)। यूज़र data directory में -`password` में salted hashes के रूप में रखे जाते हैं और traffic66 मशीन पर एक -ही command से संभाले जाते हैं (जब traffic66 `-data …` के साथ चलता है तो उसे -जोड़ें): +नए installation में `admin` / `traffic66` से साइन इन होता है, और पहला साइन इन +कुछ भी और दिखाने से पहले नया पासवर्ड माँगता है (demo `traffic66` ही रखता है)। +इसके बाद menu के नीचे **खाता** में आप अपना पासवर्ड बदलते हैं; administrator +(`admin`) वहीं यूज़र्स जोड़ और हटा भी सकता है और उनके पासवर्ड reset कर सकता है। +LDAP / Active Directory से साइन इन विकास में है। + +यूज़र data directory में `password` में salted hashes के रूप में रखे जाते हैं। +यही काम traffic66 मशीन पर एक command से भी किया जा सकता है (जब traffic66 +`-data …` के साथ चलता है तो उसे जोड़ें): | काम | Command | |---|---| @@ -139,8 +141,8 @@ Start-ScheduledTask -TaskName traffic66 | `alice` को हटाएँ | `traffic66 passwd -user alice -delete` | | यूज़र्स की सूची देखें | `traffic66 passwd -list` | -बदलाव तुरंत लागू होते हैं। सभी यूज़र्स के अधिकार एक जैसे हैं। scripts और -containers के लिए `TRAFFIC66_PASSWORD=…` (या `-password`) उस run के लिए केवल +बदलाव तुरंत लागू होते हैं। यूज़र्स के प्रबंधन को छोड़कर सभी यूज़र्स के अधिकार +एक जैसे हैं। scripts और containers के लिए `TRAFFIC66_PASSWORD=…` (या `-password`) उस run के लिए केवल `-user` को उसी पासवर्ड के साथ स्वीकार करता है। एक मिनट में पाँच ग़लत पासवर्ड होने पर उस address को एक मिनट के लिए block कर दिया जाता है। diff --git a/docs/README.id.md b/docs/README.id.md index d48c996..925a05d 100644 --- a/docs/README.id.md +++ b/docs/README.id.md @@ -49,12 +49,12 @@ Unduh arsip untuk sistem Anda dari (Windows x64, Linux x86-64/ARM64 dengan kernel 3.2+, macOS 11+), ekstrak, lalu jalankan: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` Di macOS, jalankan dulu `xattr -dr com.apple.quarantine `. Buka -http://127.0.0.1:8066 sebagai `admin` / `try66`: riwayat satu hari dan +http://127.0.0.1:8066 sebagai `admin` / `traffic66`: riwayat satu hari dan trafik live dari empat perangkat simulasi, termasuk sebuah serangan yang ditampilkan langkah demi langkah di **Temuan**. Ctrl+C menghentikannya; hapus `traffic66-demo` untuk memulai dari awal. Untuk menjalankannya berdampingan @@ -113,8 +113,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -Klik dua kali `traffic66.exe` juga bisa: itu membuka antarmuka web dan -menampilkan kata sandi pertama di jendelanya. +Klik dua kali `traffic66.exe` juga bisa: itu membuka antarmuka web. **macOS**: ekstrak ke `/usr/local/traffic66`, hapus tanda karantina, jalankan `traffic66 passwd -data "/Library/Application Support/traffic66"`, @@ -125,12 +124,16 @@ lalu jalankan dari LaunchDaemon yang `ProgramArguments`-nya adalah program, ## 3. User dan kata sandi -Pada start pertama, traffic66 membuat user `admin` dengan kata sandi acak -dan menampilkannya sekali (di jendela, di terminal, atau -`journalctl -u traffic66 | grep "first start"`). User disimpan sebagai -salted hash di `password` di direktori data dan dikelola dengan satu -perintah di mesin traffic66 (tambahkan `-data …` jika traffic66 berjalan -dengannya): +Instalasi baru masuk sebagai `admin` / `traffic66`, dan login pertama +meminta kata sandi baru sebelum menampilkan apa pun (demo tetap memakai +`traffic66`). Setelah itu, **Akun** di bagian bawah menu mengganti kata +sandi Anda; administrator (`admin`) juga menambah dan menghapus user serta +me-reset kata sandi mereka di sana. Login dengan LDAP / Active Directory +sedang dikembangkan. + +User disimpan sebagai salted hash di `password` di direktori data. Hal yang +sama bisa dilakukan di mesin traffic66 dengan satu perintah (tambahkan +`-data …` jika traffic66 berjalan dengannya): | Untuk | Perintah | |---|---| @@ -139,8 +142,8 @@ dengannya): | Menghapus `alice` | `traffic66 passwd -user alice -delete` | | Menampilkan daftar user | `traffic66 passwd -list` | -Perubahan langsung berlaku. Semua user punya hak yang sama. Untuk skrip dan -container, `TRAFFIC66_PASSWORD=…` (atau `-password`) hanya menerima `-user` +Perubahan langsung berlaku. Selain pengelolaan user, semua user punya hak +yang sama. Untuk skrip dan container, `TRAFFIC66_PASSWORD=…` (atau `-password`) hanya menerima `-user` dengan kata sandi itu untuk run tersebut. Lima kali salah kata sandi dalam satu menit memblokir alamat itu selama satu menit. diff --git a/docs/README.ja.md b/docs/README.ja.md index 60b764e..0f6b4cf 100644 --- a/docs/README.ja.md +++ b/docs/README.ja.md @@ -40,11 +40,11 @@ ntopng、ElastiFlow、pmacct と Grafana の組み合わせ、あるいは PRTG [Releases ページ](https://github.com/githubflyideas/traffic66/releases) から自分のシステム用のアーカイブ(Windows x64、カーネル 3.2 以上の Linux x86-64/ARM64、macOS 11 以上)をダウンロードし、展開して実行します: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` -macOS では先に `xattr -dr com.apple.quarantine ` を実行してください。http://127.0.0.1:8066 を開いて `admin` / `try66` でサインインすると、1 日分の履歴と、シミュレートした 4 台の機器からのライブトラフィックが表示されます。その中には攻撃が含まれ、**検知** で段階ごとに示されます。Ctrl+C で停止し、`traffic66-demo` を削除すると最初からやり直せます。本番インストールと並べて動かすには:`-addr :8067 -listen ""`。 +macOS では先に `xattr -dr com.apple.quarantine ` を実行してください。http://127.0.0.1:8066 を開いて `admin` / `traffic66` でサインインすると、1 日分の履歴と、シミュレートした 4 台の機器からのライブトラフィックが表示されます。その中には攻撃が含まれ、**検知** で段階ごとに示されます。Ctrl+C で停止し、`traffic66-demo` を削除すると最初からやり直せます。本番インストールと並べて動かすには:`-addr :8067 -listen ""`。 @@ -96,7 +96,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -`traffic66.exe` をダブルクリックしても動きます。Web UI が開き、初回のパスワードがウィンドウに表示されます。 +`traffic66.exe` をダブルクリックしても動きます。Web UI が開きます。 **macOS**:`/usr/local/traffic66` に展開し、quarantine フラグを外し、`traffic66 passwd -data "/Library/Application Support/traffic66"` を実行して、LaunchDaemon から起動します。`ProgramArguments` にはプログラム、`-data`、そのディレクトリを指定し、`RunAtLoad` と `KeepAlive` を付けます。 @@ -104,7 +104,9 @@ Start-ScheduledTask -TaskName traffic66 ## 3. ユーザーとパスワード -初回起動時、traffic66 はユーザー `admin` をランダムなパスワードで作成し、一度だけ表示します(ウィンドウ、ターミナル、または `journalctl -u traffic66 | grep "first start"`)。ユーザーはソルト付きハッシュとしてデータディレクトリの `password` に保存され、traffic66 のマシン上で 1 つのコマンドで管理します(traffic66 を `-data …` 付きで動かしている場合は同じものを付けます): +新規インストールでは `admin` / `traffic66` でサインインします。初回のサインインでは、ほかの画面を表示する前に新しいパスワードの設定を求められます(デモは `traffic66` のままです)。以降は、メニュー下部の **アカウント** で自分のパスワードを変更できます。管理者(`admin`)はそこでユーザーの追加・削除とパスワードのリセットも行えます。LDAP / Active Directory によるサインインは開発中です。 + +ユーザーはソルト付きハッシュとしてデータディレクトリの `password` に保存されます。同じ操作は traffic66 のマシン上で 1 つのコマンドでも行えます(traffic66 を `-data …` 付きで動かしている場合は同じものを付けます): | 操作 | コマンド | |---|---| @@ -113,7 +115,7 @@ Start-ScheduledTask -TaskName traffic66 | `alice` を削除 | `traffic66 passwd -user alice -delete` | | ユーザーの一覧 | `traffic66 passwd -list` | -変更はすぐに反映されます。すべてのユーザーの権限は同じです。スクリプトやコンテナでは、`TRAFFIC66_PASSWORD=…`(または `-password`)を指定すると、その実行では `-user` とそのパスワードだけを受け付けます。1 分間に 5 回パスワードを間違えると、そのアドレスは 1 分間ブロックされます。 +変更はすぐに反映されます。ユーザー管理を除き、すべてのユーザーの権限は同じです。スクリプトやコンテナでは、`TRAFFIC66_PASSWORD=…`(または `-password`)を指定すると、その実行では `-user` とそのパスワードだけを受け付けます。1 分間に 5 回パスワードを間違えると、そのアドレスは 1 分間ブロックされます。 diff --git a/docs/README.ko.md b/docs/README.ko.md index 3bc3223..dbe2ea0 100644 --- a/docs/README.ko.md +++ b/docs/README.ko.md @@ -40,11 +40,11 @@ ntopng, ElastiFlow, pmacct와 Grafana 조합, 또는 PRTG와 SolarWinds NTA의 [Releases 페이지](https://github.com/githubflyideas/traffic66/releases)에서 시스템에 맞는 압축 파일(Windows x64, 커널 3.2 이상의 Linux x86-64/ARM64, macOS 11 이상)을 내려받아 풀고 실행합니다: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` -macOS에서는 먼저 `xattr -dr com.apple.quarantine `를 실행하십시오. http://127.0.0.1:8066을 열고 `admin` / `try66`으로 로그인하면, 하루치 이력과 가상 장비 네 대의 실시간 트래픽이 보입니다. 여기에는 공격이 하나 포함되어 있으며 **탐지**에서 단계별로 보여 줍니다. Ctrl+C로 중지하고, `traffic66-demo`를 삭제하면 처음부터 다시 시작합니다. 실제 설치와 나란히 실행하려면: `-addr :8067 -listen ""`. +macOS에서는 먼저 `xattr -dr com.apple.quarantine `를 실행하십시오. http://127.0.0.1:8066을 열고 `admin` / `traffic66`으로 로그인하면, 하루치 이력과 가상 장비 네 대의 실시간 트래픽이 보입니다. 여기에는 공격이 하나 포함되어 있으며 **탐지**에서 단계별로 보여 줍니다. Ctrl+C로 중지하고, `traffic66-demo`를 삭제하면 처음부터 다시 시작합니다. 실제 설치와 나란히 실행하려면: `-addr :8067 -listen ""`. @@ -96,7 +96,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -`traffic66.exe`를 더블클릭해도 됩니다. 웹 UI가 열리고, 첫 비밀번호가 창에 표시됩니다. +`traffic66.exe`를 더블클릭해도 됩니다. 웹 UI가 열립니다. **macOS**: `/usr/local/traffic66`에 압축을 풀고, quarantine 플래그를 제거하고, `traffic66 passwd -data "/Library/Application Support/traffic66"`를 실행한 뒤, LaunchDaemon으로 시작합니다. `ProgramArguments`는 프로그램, `-data`, 그 디렉터리로 하고 `RunAtLoad`와 `KeepAlive`를 지정합니다. @@ -104,7 +104,9 @@ Start-ScheduledTask -TaskName traffic66 ## 3. 사용자와 비밀번호 -처음 시작할 때 traffic66은 임의의 비밀번호로 사용자 `admin`을 만들고 그 비밀번호를 한 번만 출력합니다(창, 터미널, 또는 `journalctl -u traffic66 | grep "first start"`). 사용자는 솔트를 적용한 해시로 데이터 디렉터리의 `password`에 저장되며, traffic66 서버에서 명령 하나로 관리합니다(traffic66을 `-data …`와 함께 실행한다면 같은 옵션을 추가): +새로 설치하면 `admin` / `traffic66`으로 로그인하며, 첫 로그인에서는 다른 화면을 보여 주기 전에 새 비밀번호를 요구합니다(데모는 `traffic66`을 유지합니다). 이후 메뉴 맨 아래의 **계정**에서 자신의 비밀번호를 바꿀 수 있고, 관리자(`admin`)는 그곳에서 사용자를 추가·삭제하고 비밀번호를 재설정할 수도 있습니다. LDAP / Active Directory 로그인은 개발 중입니다. + +사용자는 솔트를 적용한 해시로 데이터 디렉터리의 `password`에 저장됩니다. 같은 작업을 traffic66 서버에서 명령 하나로도 할 수 있습니다(traffic66을 `-data …`와 함께 실행한다면 같은 옵션을 추가): | 작업 | 명령 | |---|---| @@ -113,7 +115,7 @@ Start-ScheduledTask -TaskName traffic66 | `alice` 삭제 | `traffic66 passwd -user alice -delete` | | 사용자 목록 보기 | `traffic66 passwd -list` | -변경은 즉시 적용됩니다. 모든 사용자의 권한은 같습니다. 스크립트와 컨테이너에서는 `TRAFFIC66_PASSWORD=…`(또는 `-password`)를 지정하면 그 실행에서는 `-user`와 그 비밀번호만 받습니다. 1분 안에 비밀번호를 다섯 번 틀리면 그 주소는 1분 동안 차단됩니다. +변경은 즉시 적용됩니다. 사용자 관리를 제외하면 모든 사용자의 권한은 같습니다. 스크립트와 컨테이너에서는 `TRAFFIC66_PASSWORD=…`(또는 `-password`)를 지정하면 그 실행에서는 `-user`와 그 비밀번호만 받습니다. 1분 안에 비밀번호를 다섯 번 틀리면 그 주소는 1분 동안 차단됩니다. diff --git a/docs/README.pt.md b/docs/README.pt.md index 8aa5bf1..6bc55f0 100644 --- a/docs/README.pt.md +++ b/docs/README.pt.md @@ -49,12 +49,12 @@ Baixe o arquivo do seu sistema na (Windows x64, Linux x86-64/ARM64 com kernel 3.2+, macOS 11+), descompacte e execute: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` No macOS, execute antes `xattr -dr com.apple.quarantine `. Abra -http://127.0.0.1:8066 como `admin` / `try66`: um dia de histórico e tráfego +http://127.0.0.1:8066 como `admin` / `traffic66`: um dia de histórico e tráfego ao vivo de quatro equipamentos simulados, incluindo um ataque mostrado etapa por etapa em **Detecções**. Ctrl+C para; apague `traffic66-demo` para recomeçar do zero. Para rodá-la ao lado de uma instalação real: @@ -115,7 +115,7 @@ Start-ScheduledTask -TaskName traffic66 ``` Um clique duplo em `traffic66.exe` também funciona: ele abre a interface -web e mostra a primeira senha na janela. +web. **macOS**: descompacte em `/usr/local/traffic66`, remova a marca de quarentena, execute `traffic66 passwd -data "/Library/Application Support/traffic66"` @@ -126,12 +126,16 @@ programa, `-data` e esse diretório, com `RunAtLoad` e `KeepAlive`. ## 3. Usuários e senhas -Na primeira execução, o traffic66 cria o usuário `admin` com uma senha -aleatória e a mostra uma única vez (na janela, no terminal ou em -`journalctl -u traffic66 | grep "first start"`). Os usuários ficam guardados -como hashes com salt em `password`, no diretório de dados, e são gerenciados -com um único comando na máquina do traffic66 (adicione `-data …` quando o -traffic66 roda com ele): +Uma instalação nova entra como `admin` / `traffic66`, e o primeiro login +pede uma nova senha antes de mostrar qualquer outra coisa (a demo mantém +`traffic66`). Depois, **Conta**, no pé do menu, troca a sua senha; o +administrador (`admin`) também adiciona e remove usuários e redefine as +senhas deles ali. O login com LDAP / Active Directory está em +desenvolvimento. + +Os usuários ficam guardados como hashes com salt em `password`, no diretório +de dados. O mesmo pode ser feito na máquina do traffic66 com um único +comando (adicione `-data …` quando o traffic66 roda com ele): | Para | Comando | |---|---| @@ -140,8 +144,8 @@ traffic66 roda com ele): | Remover `alice` | `traffic66 passwd -user alice -delete` | | Listar os usuários | `traffic66 passwd -list` | -As mudanças valem na hora. Todos os usuários têm os mesmos direitos. Para -scripts e contêineres, `TRAFFIC66_PASSWORD=…` (ou `-password`) aceita só +As mudanças valem na hora. Fora a gestão de usuários, todos os usuários têm +os mesmos direitos. Para scripts e contêineres, `TRAFFIC66_PASSWORD=…` (ou `-password`) aceita só `-user` com essa senha naquela execução. Cinco senhas erradas em um minuto bloqueiam o endereço por um minuto. diff --git a/docs/README.ru.md b/docs/README.ru.md index 195c9cb..83c6464 100644 --- a/docs/README.ru.md +++ b/docs/README.ru.md @@ -49,12 +49,12 @@ Grafana или модулям анализа потоков PRTG и SolarWinds N (Windows x64, Linux x86-64/ARM64 с ядром 3.2+, macOS 11+), распакуйте его и запустите: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` В macOS сначала выполните `xattr -dr com.apple.quarantine `. Откройте -http://127.0.0.1:8066 и войдите как `admin` / `try66`: сутки истории и живой +http://127.0.0.1:8066 и войдите как `admin` / `traffic66`: сутки истории и живой трафик от четырёх имитируемых устройств, включая атаку, показанную шаг за шагом на странице **Обнаружения**. Ctrl+C останавливает демо; удалите `traffic66-demo`, чтобы начать заново. Чтобы запустить его рядом с @@ -114,7 +114,7 @@ Start-ScheduledTask -TaskName traffic66 ``` Можно также просто дважды щёлкнуть `traffic66.exe`: он откроет -веб-интерфейс и покажет пароль первого запуска в своём окне. +веб-интерфейс. **macOS**: распакуйте в `/usr/local/traffic66`, снимите флаг карантина, выполните `traffic66 passwd -data "/Library/Application Support/traffic66"` и @@ -125,12 +125,16 @@ Start-ScheduledTask -TaskName traffic66 ## 3. Пользователи и пароли -При первом запуске traffic66 создаёт пользователя `admin` со случайным -паролем и выводит его один раз (в окне, в терминале или в -`journalctl -u traffic66 | grep "first start"`). Пользователи хранятся в виде -хешей с солью в файле `password` в каталоге данных и управляются одной -командой на машине с traffic66 (добавьте `-data …`, если traffic66 запущен -с этим параметром): +В новой установке вход выполняется как `admin` / `traffic66`, и при первом +входе, прежде чем показать что-либо ещё, traffic66 просит задать новый +пароль (в демо остаётся `traffic66`). Затем пароль меняется в разделе +**Учётная запись** внизу меню; администратор (`admin`) там же добавляет и +удаляет пользователей и сбрасывает их пароли. Вход через LDAP / Active +Directory в разработке. + +Пользователи хранятся в виде хешей с солью в файле `password` в каталоге +данных. То же самое можно сделать одной командой на машине с traffic66 +(добавьте `-data …`, если traffic66 запущен с этим параметром): | Задача | Команда | |---|---| @@ -139,8 +143,8 @@ Start-ScheduledTask -TaskName traffic66 | Удалить пользователя `alice` | `traffic66 passwd -user alice -delete` | | Вывести список пользователей | `traffic66 passwd -list` | -Изменения применяются сразу. У всех пользователей одинаковые права. Для -скриптов и контейнеров `TRAFFIC66_PASSWORD=…` (или `-password`) на этот +Изменения применяются сразу. Кроме управления пользователями, у всех +пользователей одинаковые права. Для скриптов и контейнеров `TRAFFIC66_PASSWORD=…` (или `-password`) на этот запуск принимает только `-user` с этим паролем. Пять неверных паролей за минуту блокируют адрес на минуту. diff --git a/docs/README.ur.md b/docs/README.ur.md index dbf71bd..5947215 100644 --- a/docs/README.ur.md +++ b/docs/README.ur.md @@ -51,12 +51,12 @@ Elasticsearch، Kafka یا الگ database کے۔ اسے unpack کریں اور چلائیں: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` macOS پر پہلے `xattr -dr com.apple.quarantine ` چلائیں۔ -http://127.0.0.1:8066 کھولیں اور `admin` / `try66` سے سائن اِن کریں: ایک دن کی +http://127.0.0.1:8066 کھولیں اور `admin` / `traffic66` سے سائن اِن کریں: ایک دن کی تاریخ اور چار simulated ڈیوائسز سے live ٹریفک، جس میں ایک حملہ بھی ہے جو **مشتبہ سرگرمی** پر قدم بہ قدم دکھایا جاتا ہے۔ Ctrl+C اسے روکتا ہے؛ نئے سرے سے شروع کرنے کے لیے `traffic66-demo` حذف کر دیں۔ اصل installation کے ساتھ ساتھ @@ -115,8 +115,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -`traffic66.exe` پر double-click بھی کام کرتا ہے: یہ web UI کھولتا ہے اور پہلا -پاس ورڈ اپنی window میں دکھاتا ہے۔ +`traffic66.exe` پر double-click بھی کام کرتا ہے: یہ web UI کھولتا ہے۔ **macOS**: `/usr/local/traffic66` میں unpack کریں، quarantine flag ہٹائیں، `traffic66 passwd -data "/Library/Application Support/traffic66"` چلائیں، اور @@ -127,12 +126,15 @@ Start-ScheduledTask -TaskName traffic66 ## 3. یوزرز اور پاس ورڈ -پہلی بار شروع ہونے پر traffic66 ایک random پاس ورڈ کے ساتھ یوزر `admin` بناتا -ہے اور اسے ایک بار دکھاتا ہے (window میں، terminal میں، یا -`journalctl -u traffic66 | grep "first start"`)۔ یوزرز data directory میں -`password` کے اندر salted hashes کی صورت میں رکھے جاتے ہیں اور traffic66 مشین -پر ایک command سے سنبھالے جاتے ہیں (اگر traffic66 `-data …` کے ساتھ چلتا ہے تو -وہ بھی شامل کریں): +نئی installation میں `admin` / `traffic66` سے سائن اِن ہوتا ہے، اور پہلا سائن اِن +کچھ اور دکھانے سے پہلے نیا پاس ورڈ مانگتا ہے (demo `traffic66` ہی رکھتا ہے)۔ اس +کے بعد menu کے نیچے **اکاؤنٹ** میں آپ اپنا پاس ورڈ بدلتے ہیں؛ administrator +(`admin`) وہیں یوزرز شامل اور حذف بھی کرتا ہے اور ان کے پاس ورڈ reset کرتا ہے۔ +LDAP / Active Directory سے سائن اِن زیرِ تیاری ہے۔ + +یوزرز data directory میں `password` کے اندر salted hashes کی صورت میں رکھے جاتے +ہیں۔ یہی کام traffic66 مشین پر ایک command سے بھی کیا جا سکتا ہے (اگر traffic66 +`-data …` کے ساتھ چلتا ہے تو وہ بھی شامل کریں): | کام | Command | |---|---| @@ -141,8 +143,8 @@ Start-ScheduledTask -TaskName traffic66 | `alice` کو حذف کرنا | `traffic66 passwd -user alice -delete` | | یوزرز کی فہرست | `traffic66 passwd -list` | -تبدیلیاں فوراً لاگو ہوتی ہیں۔ تمام یوزرز کے اختیارات یکساں ہیں۔ scripts اور -containers کے لیے `TRAFFIC66_PASSWORD=…` (یا `-password`) اس run میں صرف اسی +تبدیلیاں فوراً لاگو ہوتی ہیں۔ یوزرز کے انتظام کے علاوہ تمام یوزرز کے اختیارات +یکساں ہیں۔ scripts اور containers کے لیے `TRAFFIC66_PASSWORD=…` (یا `-password`) اس run میں صرف اسی پاس ورڈ کے ساتھ `-user` کو قبول کرتا ہے۔ ایک منٹ میں پانچ غلط پاس ورڈ اس address کو ایک منٹ کے لیے block کر دیتے ہیں۔ diff --git a/docs/README.zh.md b/docs/README.zh.md index 4502d89..20dfba7 100644 --- a/docs/README.zh.md +++ b/docs/README.zh.md @@ -40,11 +40,11 @@ 从 [Releases 页面](https://github.com/githubflyideas/traffic66/releases) 下载对应系统的压缩包(Windows x64、内核 3.2 及以上的 Linux x86-64/ARM64、macOS 11 及以上),解压后运行: ``` -./traffic66 demo -password try66 # Linux, macOS -.\traffic66.exe demo -password try66 # Windows +./traffic66 demo # Linux, macOS +.\traffic66.exe demo # Windows ``` -在 macOS 上请先运行 `xattr -dr com.apple.quarantine `。打开 http://127.0.0.1:8066,用 `admin` / `try66` 登录:可以看到一天的历史数据和四台模拟设备的实时流量,其中包括一次攻击,在 **发现** 中一步步展示。按 Ctrl+C 停止;删除 `traffic66-demo` 即可从头开始。要和正式安装同时运行:`-addr :8067 -listen ""`。 +在 macOS 上请先运行 `xattr -dr com.apple.quarantine `。打开 http://127.0.0.1:8066,用 `admin` / `traffic66` 登录:可以看到一天的历史数据和四台模拟设备的实时流量,其中包括一次攻击,在 **发现** 中一步步展示。按 Ctrl+C 停止;删除 `traffic66-demo` 即可从头开始。要和正式安装同时运行:`-addr :8067 -listen ""`。 @@ -96,7 +96,7 @@ Register-ScheduledTask -TaskName traffic66 -Action $a -Trigger (New-ScheduledTas Start-ScheduledTask -TaskName traffic66 ``` -也可以直接双击 `traffic66.exe`:它会打开 Web 界面,并在窗口中显示首次启动的密码。 +也可以直接双击 `traffic66.exe`:它会打开 Web 界面。 **macOS**:解压到 `/usr/local/traffic66`,去掉隔离标记,运行 `traffic66 passwd -data "/Library/Application Support/traffic66"`,然后用一个 LaunchDaemon 启动它:其 `ProgramArguments` 为程序、`-data` 和该目录,并设置 `RunAtLoad` 和 `KeepAlive`。 @@ -104,7 +104,9 @@ Start-ScheduledTask -TaskName traffic66 ## 3. 用户与密码 -traffic66 首次启动时会创建用户 `admin`,设置一个随机密码并只显示一次(在窗口、终端中,或通过 `journalctl -u traffic66 | grep "first start"` 查看)。用户以加盐哈希的形式保存在数据目录下的 `password` 文件中,在 traffic66 主机上用一条命令管理(如果 traffic66 运行时使用了 `-data …`,命令中也要加上): +新安装的 traffic66 用 `admin` / `traffic66` 登录,首次登录会先要求设置新密码,之后才显示其他内容(演示模式保持 `traffic66`)。此后在菜单底部的 **账户** 中修改自己的密码;管理员(`admin`)还可以在那里添加和删除用户,并重置他们的密码。通过 LDAP / Active Directory 登录正在开发中。 + +用户以加盐哈希的形式保存在数据目录下的 `password` 文件中。同样的操作也可以在 traffic66 主机上用一条命令完成(如果 traffic66 运行时使用了 `-data …`,命令中也要加上): | 操作 | 命令 | |---|---| @@ -113,7 +115,7 @@ traffic66 首次启动时会创建用户 `admin`,设置一个随机密码并 | 删除 `alice` | `traffic66 passwd -user alice -delete` | | 列出用户 | `traffic66 passwd -list` | -更改立即生效。所有用户权限相同。用于脚本和容器时,`TRAFFIC66_PASSWORD=…`(或 `-password`)使本次运行只接受 `-user` 及该密码。同一地址在一分钟内输错五次密码,会被封禁一分钟。 +更改立即生效。除管理用户外,所有用户权限相同。用于脚本和容器时,`TRAFFIC66_PASSWORD=…`(或 `-password`)使本次运行只接受 `-user` 及该密码。同一地址在一分钟内输错五次密码,会被封禁一分钟。 diff --git a/internal/api/accounts.go b/internal/api/accounts.go new file mode 100644 index 0000000..b7674bc --- /dev/null +++ b/internal/api/accounts.go @@ -0,0 +1,160 @@ +package api + +import ( + "encoding/json" + "errors" + "net/http" + "strings" + + "github.com/githubflyideas/traffic66/internal/auth" +) + +// loginHint tells the sign-in page whether to show the default password: +// always in the demo, and while some user of a new installation still has it. +func (s *Server) loginHint(w http.ResponseWriter, r *http.Request) { + def := s.Demo || (s.Accounts != nil && s.Accounts.HasDefault()) + out := map[string]any{"demo": s.Demo, "default": def} + if def { + out["user"], out["password"] = "admin", auth.DefaultPassword + } + writeJSON(w, http.StatusOK, out) +} + +// me describes the signed-in user: whether they manage the others, whether +// they must change the default password, and whether passwords can be +// changed here at all (not when given with -password). +func (s *Server) me(w http.ResponseWriter, r *http.Request) { + u := userOf(r) + out := map[string]any{"user": u, "admin": false, "must_change": false, "fixed": true, "ldap": false} + if s.Accounts != nil { + out["fixed"] = s.Accounts.Fixed() + out["admin"] = u != "" && u == s.Accounts.Admin() + } + if c, err := r.Cookie(cookieName); err == nil { + s.mu.Lock() + out["must_change"] = s.sessions[c.Value].mustChange + s.mu.Unlock() + } + writeJSON(w, http.StatusOK, out) +} + +// accounts returns the password file when it can be changed from here. +func (s *Server) accounts(w http.ResponseWriter) *auth.FileChecker { + if s.Accounts == nil || s.Accounts.Fixed() || s.DataDir == "" { + writeJSON(w, http.StatusConflict, map[string]string{"error": "fixed"}) + return nil + } + return s.Accounts +} + +func checkNew(pw string) error { + if len([]rune(pw)) < auth.MinLength { + return errors.New("too_short") + } + if pw == auth.DefaultPassword { + return errors.New("default") + } + return nil +} + +// changePassword sets the signed-in user's own password; the old one is +// asked for again. +func (s *Server) changePassword(w http.ResponseWriter, r *http.Request) { + a := s.accounts(w) + if a == nil { + return + } + var in struct{ Old, New string } + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 4096)).Decode(&in); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "bad request"}) + return + } + u := userOf(r) + if u == "" || !s.checkPassword(u, in.Old) { + writeJSON(w, http.StatusForbidden, map[string]string{"error": "wrong_old"}) + return + } + if err := checkNew(in.New); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()}) + return + } + if err := auth.Set(s.DataDir, u, in.New); err != nil { + fail(w, err) + return + } + s.mu.Lock() + for k, se := range s.sessions { + if se.user == u { + se.mustChange = false + s.sessions[k] = se + } + } + s.mu.Unlock() + writeJSON(w, http.StatusOK, map[string]string{}) +} + +// admin answers 403 unless the signed-in user manages the others. +func (s *Server) admin(w http.ResponseWriter, r *http.Request) *auth.FileChecker { + a := s.accounts(w) + if a == nil { + return nil + } + if u := userOf(r); u == "" || u != a.Admin() { + writeJSON(w, http.StatusForbidden, map[string]string{"error": "not_admin"}) + return nil + } + return a +} + +func (s *Server) listUsers(w http.ResponseWriter, r *http.Request) { + a := s.admin(w, r) + if a == nil { + return + } + writeJSON(w, http.StatusOK, map[string]any{"users": a.Users(), "admin": a.Admin()}) +} + +// putUser adds a user, or sets a new password for one. +func (s *Server) putUser(w http.ResponseWriter, r *http.Request) { + if s.admin(w, r) == nil { + return + } + var in struct{ User, Password string } + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 4096)).Decode(&in); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "bad request"}) + return + } + in.User = strings.TrimSpace(in.User) + if in.User == "" || strings.ContainsAny(in.User, ": \t\r\n") || len(in.User) > 64 { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "bad_user"}) + return + } + if err := checkNew(in.Password); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()}) + return + } + if err := auth.Set(s.DataDir, in.User, in.Password); err != nil { + fail(w, err) + return + } + writeJSON(w, http.StatusOK, map[string]string{}) +} + +// deleteUser removes a user; their open sessions end. The administrator +// cannot remove themselves. +func (s *Server) deleteUser(w http.ResponseWriter, r *http.Request) { + a := s.admin(w, r) + if a == nil { + return + } + u := r.URL.Query().Get("user") + if u == userOf(r) { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "self"}) + return + } + if err := auth.Delete(s.DataDir, u); err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()}) + return + } + writeJSON(w, http.StatusOK, map[string]string{}) +} diff --git a/internal/api/accounts_test.go b/internal/api/accounts_test.go new file mode 100644 index 0000000..5553c86 --- /dev/null +++ b/internal/api/accounts_test.go @@ -0,0 +1,92 @@ +package api + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/githubflyideas/traffic66/internal/auth" +) + +// A new installation signs in with the default password, must change it +// before anything else, and its admin manages the other users. +func TestAccounts(t *testing.T) { + dir := t.TempDir() + if err := auth.Set(dir, "admin", auth.DefaultPassword); err != nil { + t.Fatal(err) + } + fc := auth.NewFileChecker(dir, nil) + s := &Server{Check: fc.Check, Exists: fc.Exists, Accounts: fc, DataDir: dir, sessions: map[string]session{}} + mux := http.NewServeMux() + mux.HandleFunc("POST /api/login", s.login) + mux.HandleFunc("GET /api/loginhint", s.loginHint) + for p, h := range map[string]http.HandlerFunc{"GET /api/me": s.me, "POST /api/password": s.changePassword, "GET /api/users": s.listUsers, + "POST /api/users": s.putUser, "DELETE /api/users": s.deleteUser, "GET /api/overview": func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }} { + mux.Handle(p, s.auth(h)) + } + call := func(method, path, body, cookie string) (*httptest.ResponseRecorder, string) { + r := httptest.NewRequest(method, path, strings.NewReader(body)) + r.RemoteAddr = "192.0.2.1:1" + if cookie != "" { + r.AddCookie(&http.Cookie{Name: cookieName, Value: cookie}) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, r) + for _, c := range w.Result().Cookies() { + if c.Name == cookieName { + cookie = c.Value + } + } + return w, cookie + } + if w, _ := call("GET", "/api/loginhint", "", ""); !strings.Contains(w.Body.String(), `"default":true`) { + t.Fatalf("hint %s", w.Body) + } + w, ck := call("POST", "/api/login", `{"user":"admin","password":"traffic66"}`, "") + if !strings.Contains(w.Body.String(), `"must_change":true`) { + t.Fatalf("login %s", w.Body) + } + if w, _ := call("GET", "/api/overview", "", ck); w.Code != 403 { + t.Fatalf("overview before change: %d", w.Code) + } + if w, _ := call("POST", "/api/password", `{"old":"traffic66","new":"short"}`, ck); w.Code != 400 { + t.Fatalf("short password: %d", w.Code) + } + if w, _ := call("POST", "/api/password", `{"old":"traffic66","new":"s3cret-pass"}`, ck); w.Code != 200 { + t.Fatalf("change: %d %s", w.Code, w.Body) + } + if w, _ := call("GET", "/api/overview", "", ck); w.Code != 200 { + t.Fatalf("overview after change: %d", w.Code) + } + if w, _ := call("GET", "/api/loginhint", "", ""); !strings.Contains(w.Body.String(), `"default":false`) { + t.Fatalf("hint after change %s", w.Body) + } + // admin adds alice; alice cannot manage users + if w, _ := call("POST", "/api/users", `{"user":"alice","password":"alice-pass"}`, ck); w.Code != 200 { + t.Fatalf("add alice: %d %s", w.Code, w.Body) + } + _, ack := call("POST", "/api/login", `{"user":"alice","password":"alice-pass"}`, "") + if w, _ := call("GET", "/api/users", "", ack); w.Code != 403 { + t.Fatalf("alice lists users: %d", w.Code) + } + if w, _ := call("GET", "/api/me", "", ack); !strings.Contains(w.Body.String(), `"admin":false`) { + t.Fatalf("alice me %s", w.Body) + } + if w, _ := call("DELETE", "/api/users?user=admin", "", ck); w.Code != 400 { + t.Fatalf("admin deletes self: %d", w.Code) + } + if w, _ := call("DELETE", "/api/users?user=alice", "", ck); w.Code != 200 { + t.Fatalf("delete alice: %d", w.Code) + } + if w, _ := call("GET", "/api/me", "", ack); w.Code != 401 { + t.Fatalf("deleted alice still signed in: %d", w.Code) + } + // -password: nothing to change here + fixed := &Server{Accounts: auth.NewFileChecker(dir, map[string]string{"admin": "x"}), DataDir: dir} + rw := httptest.NewRecorder() + fixed.changePassword(rw, httptest.NewRequest("POST", "/api/password", strings.NewReader(`{}`))) + if rw.Code != 409 { + t.Fatalf("fixed: %d", rw.Code) + } +} diff --git a/internal/api/server.go b/internal/api/server.go index 421d483..4110884 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -2,6 +2,7 @@ package api import ( + "context" "crypto/rand" "crypto/subtle" "encoding/hex" @@ -16,6 +17,7 @@ import ( "sync" "time" + "github.com/githubflyideas/traffic66/internal/auth" "github.com/githubflyideas/traffic66/internal/collector" "github.com/githubflyideas/traffic66/internal/detect" "github.com/githubflyideas/traffic66/internal/dnsres" @@ -48,6 +50,9 @@ type Server struct { Check func(user, pw string) bool // login check; replaces Users when set Exists func(user string) bool // whether a user still exists; signed-in sessions of deleted users end LocalTok string // token for the TUI on this machine + // Accounts is the password file: changing passwords and managing users + // from the web UI. Nil in tests and when it does not apply. + Accounts *auth.FileChecker Capture func() []CaptureInfo SNMP func() []snmp.Status Started time.Time @@ -62,8 +67,23 @@ type Server struct { type session struct { user string exp time.Time + // mustChange: signed in with the default password; only changing it + // is allowed until then + mustChange bool } +type ctxKey struct{} + +// userOf returns the signed-in user of a request ("" for the TUI token). +func userOf(r *http.Request) string { + u, _ := r.Context().Value(ctxKey{}).(string) + return u +} + +// allowedBeforeChange are the calls a session that must change its +// password may make. +var allowedBeforeChange = map[string]bool{"/api/me": true, "/api/password": true, "/api/status": true} + // CaptureInfo describes a local capture interface. type CaptureInfo struct { Iface string `json:"iface"` @@ -88,8 +108,14 @@ func (s *Server) Handler() http.Handler { mux.HandleFunc("POST /api/logout", s.logout) mux.HandleFunc("GET /auto", s.autoLogin) mux.HandleFunc("GET /logo", s.logo) + mux.HandleFunc("GET /api/loginhint", s.loginHint) api := func(pattern string, h http.HandlerFunc) { mux.Handle(pattern, s.auth(h)) } api("GET /api/status", s.status) + api("GET /api/me", s.me) + api("POST /api/password", s.changePassword) + api("GET /api/users", s.listUsers) + api("POST /api/users", s.putUser) + api("DELETE /api/users", s.deleteUser) api("GET /api/overview", s.data((*Server).overview)) api("GET /api/topn", s.data((*Server).topn)) api("GET /api/sankey", s.data((*Server).sankey)) @@ -230,7 +256,7 @@ func (s *Server) auth(next http.HandlerFunc) http.Handler { return } s.fails.ok(ip) - next(w, r) + next(w, r.WithContext(context.WithValue(r.Context(), ctxKey{}, u))) return } if c, err := r.Cookie(cookieName); err == nil { @@ -244,7 +270,11 @@ func (s *Server) auth(next http.HandlerFunc) http.Handler { se.exp = time.Now().Add(12 * time.Hour) s.sessions[c.Value] = se s.mu.Unlock() - next(w, r) + if se.mustChange && !allowedBeforeChange[r.URL.Path] { + writeJSON(w, http.StatusForbidden, map[string]string{"error": "change_password"}) + return + } + next(w, r.WithContext(context.WithValue(r.Context(), ctxKey{}, se.user))) return } s.mu.Unlock() @@ -289,10 +319,13 @@ func (s *Server) login(w http.ResponseWriter, r *http.Request) { delete(s.sessions, k) } } - s.sessions[tok] = session{in.User, now.Add(12 * time.Hour)} + // the default password of a new installation is changed before + // anything else (not in the demo, nor with -password) + must := !s.Demo && s.Accounts != nil && !s.Accounts.Fixed() && in.Password == auth.DefaultPassword + s.sessions[tok] = session{user: in.User, exp: now.Add(12 * time.Hour), mustChange: must} s.mu.Unlock() http.SetCookie(w, &http.Cookie{Name: cookieName, Value: tok, Path: "/", HttpOnly: true, SameSite: http.SameSiteStrictMode}) - writeJSON(w, http.StatusOK, map[string]string{"user": in.User}) + writeJSON(w, http.StatusOK, map[string]any{"user": in.User, "must_change": must}) } // autoLogin signs in with the one-time token printed for traffic66 file.pcap @@ -306,7 +339,7 @@ func (s *Server) autoLogin(w http.ResponseWriter, r *http.Request) { b := make([]byte, 24) rand.Read(b) tok := hex.EncodeToString(b) - s.sessions[tok] = session{"admin", time.Now().Add(12 * time.Hour)} + s.sessions[tok] = session{user: "admin", exp: time.Now().Add(12 * time.Hour)} http.SetCookie(w, &http.Cookie{Name: cookieName, Value: tok, Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode}) } s.mu.Unlock() diff --git a/internal/auth/auth.go b/internal/auth/auth.go index ffc240f..4deb770 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -23,6 +23,14 @@ import ( // FileName is the password file inside the data directory. const FileName = "password" +// DefaultPassword is the password of the first user of a new installation +// and of the demo. A new installation asks for a new one at the first +// sign-in, before anything else is shown. +const DefaultPassword = "traffic66" + +// MinLength is the shortest password accepted from the web UI. +const MinLength = 8 + const iterations = 210000 var b64 = base64.RawStdEncoding @@ -202,6 +210,9 @@ type FileChecker struct { mu sync.Mutex stamp string c *Checker + // HasDefault's answer and the file state it was computed for + def bool + defStamp string } // NewFileChecker returns a checker for dir; fixed may be nil. @@ -235,6 +246,69 @@ func (f *FileChecker) Check(user, pw string) bool { return c.Check(user, pw) } +// Fixed reports whether the passwords were given at start (-password or +// TRAFFIC66_PASSWORD) rather than kept in the password file. +func (f *FileChecker) Fixed() bool { return f.fixed != nil } + +// Users lists the users in the password file, in file order. +func (f *FileChecker) Users() []string { + if f.fixed != nil { + var out []string + for u := range f.fixed { + out = append(out, u) + } + return out + } + es, _ := Load(f.dir) + var out []string + for _, e := range es { + out = append(out, e.User) + } + return out +} + +// Admin is the user who manages the others: "admin" when there is one, +// else the first user in the file. +func (f *FileChecker) Admin() string { + us := f.Users() + for _, u := range us { + if u == "admin" { + return u + } + } + if len(us) > 0 { + return us[0] + } + return "" +} + +// HasDefault reports whether some user still has DefaultPassword, for the +// hint on the sign-in page. It is checked once per change of the file. +func (f *FileChecker) HasDefault() bool { + if f.fixed != nil { + return false + } + stamp := "" + if fi, err := os.Stat(filepath.Join(f.dir, FileName)); err == nil { + stamp = fmt.Sprint(fi.ModTime().UnixNano(), fi.Size()) + } + f.mu.Lock() + defer f.mu.Unlock() + if f.defStamp == stamp && stamp != "" { + return f.def + } + es, _ := Load(f.dir) + f.def = false + for _, e := range es { + if Verify(e.Hash, DefaultPassword) { + f.def = true + break + } + } + f.defStamp = stamp + return f.def +} + // Exists reports whether user can currently sign in. func (f *FileChecker) Exists(user string) bool { if f.fixed != nil { diff --git a/internal/web/static/app.css b/internal/web/static/app.css index 01e6458..d3604bf 100644 --- a/internal/web/static/app.css +++ b/internal/web/static/app.css @@ -94,8 +94,8 @@ nav.dash button[aria-current="page"] .ni{opacity:1} #theme i{width:12px;height:12px;border-radius:50%;background:var(--accent);box-shadow:inset 0 0 0 2px var(--surface),0 0 0 1px var(--line-2)} .foot select{width:100%;min-width:0;color:var(--ink);border:1px solid var(--line-2);border-radius:6px;padding:4px 6px;background:var(--surface);font-size:13px} .linkbtn{border:0;background:none;color:var(--ink-3);cursor:pointer;font-size:12.5px;padding:2px;white-space:nowrap} -#logout{display:flex;align-items:center;gap:8px;font-size:13.5px;font-weight:600;padding:6px 4px;border-radius:6px} -#logout .ni{width:16px;height:16px} +#logout,#acct{display:flex;align-items:center;gap:8px;font-size:13.5px;font-weight:600;padding:6px 4px;border-radius:6px} +#logout .ni,#acct .ni{width:16px;height:16px} .linkbtn:hover{color:var(--accent-ink)} .main{min-width:0} @@ -366,3 +366,14 @@ label.btn.disabled{opacity:.5;cursor:not-allowed!important} .iflist .ifrow.peer>button:first-child .muted{font-size:10.5px!important} .iflist .ifrow.peer>button:first-child .st{font-size:11px;opacity:.75} #sbbar select{border:1px solid var(--line-2);border-radius:7px;padding:4px 8px;background:var(--surface);color:var(--ink);font-weight:600;max-width:260px} +.login .hint{margin:0;padding:8px 10px;border-radius:8px;background:var(--accent-soft);color:var(--ink);font-size:13.5px} +.acctform{display:grid;gap:10px;max-width:420px} +.acctform label{display:grid;gap:4px;font-size:13px;color:var(--ink-3)} +.acctform input,.acctinline input{border:1px solid var(--line-2);border-radius:7px;padding:7px 9px;background:var(--surface);color:var(--ink);font-size:14px} +.acctform.ldap{opacity:.55} +.acctmust{margin:0 0 12px;padding:10px 12px;border-radius:8px;background:var(--accent-soft);color:var(--ink);font-weight:600} +.accttable{width:100%;margin-bottom:12px} +.accttable td{padding:8px 4px;border-bottom:1px solid var(--line)} +.acctinline{display:flex;gap:6px;flex-wrap:wrap;align-items:center} +.acctinline input{min-width:0;flex:1 1 140px} +body.mustchange #nav,body.mustchange #acct,body.mustchange .ifsel{display:none} diff --git a/internal/web/static/app.js b/internal/web/static/app.js index 90c2f8e..97a2918 100644 --- a/internal/web/static/app.js +++ b/internal/web/static/app.js @@ -147,7 +147,7 @@ async function resolveNames() { } // ------------------------------------------------------------ state & API -const VIEWS = ['overview', 'findings', 'topn', 'traffic', 'sankey', 'geo', 'threats', 'records', 'cleanup', 'sandbox', 'ifaces', 'sources', 'detail']; +const VIEWS = ['account', 'overview', 'findings', 'topn', 'traffic', 'sankey', 'geo', 'threats', 'records', 'cleanup', 'sandbox', 'ifaces', 'sources', 'detail']; const RANGES = ['15m', '1h', '6h', '24h', '7d', '30d']; const state = {v: 'overview', r: '24h', f: [], dim: 'conv', ifc: null, ifdir: 'both', sort: {k: 'wire', asc: false}, det: null}; function readHash() { @@ -237,6 +237,7 @@ async function api(path, extra = {}, filters = state.f) { if (filters.length) p.set('f', JSON.stringify(filters.map(x => ({f: x.f, v: x.v, neg: x.neg})))); const res = await fetch('/api/' + path + '?' + p); if (res.status === 401) { showLogin(); throw new Error('login'); } + if (res.status === 403 && me) { const j = await res.clone().json().catch(() => ({})); if (j.error === 'change_password') { me.must_change = true; go('account'); throw new Error('login'); } } const j = await res.json().catch(() => ({})); if (!res.ok) { const e = new Error(j.error || res.statusText); e.kind = j.kind; throw e; } return j; @@ -1201,6 +1202,70 @@ views.sandbox = async (el) => { // in offline mode the first visit goes on to the overview once the files are in let sbWaited = true; +// Account: one's own password; for the administrator also the users, and +// the coming LDAP / AD sign-in. +views.account = async (el) => { + await loadMe(); + const m = me || {}; + const pwForm = m.fixed ? `

${esc(t('acct.fixed'))}

` : ` + ${m.must_change ? `

${esc(t('acct.must'))}

` : ''} +
+ + + +

${esc(t('acct.rule'))}

+
+
`; + let users = null; + if (m.admin && !m.fixed && !m.must_change) { + const r = await fetch('/api/users'); + if (r.ok) users = await r.json(); + } + const userRows = users ? users.users.map(u => `${esc(u)}${u === users.admin ? ` ${t('acct.admin_tag')}` : ''} + ${u === users.admin ? '' : ` `} +
`).join('') : ''; + el.innerHTML = `
+ ${panel(users ? 'c6' : 'c12', t('acct.title_pw'), esc(m.user || ''), pwForm)} + ${users ? panel('c6', t('acct.users'), t('acct.users_sub'), `${userRows}
+
+

`) : ''} + ${users ? panel('c12', t('acct.ldap'), t('acct.dev'), `

${esc(t('acct.ldap_dev'))}

+
+
`) : ''} +
`; + const errText = c => t('acct.err_' + c) !== 'acct.err_' + c ? t('acct.err_' + c) : c; + const say = (id, ok, text) => { const n = $(id); if (n) { n.style.color = ok ? 'var(--good)' : 'var(--crit)'; n.textContent = text; } }; + const pf = $('#pwForm'); + if (pf) pf.onsubmit = async e => { + e.preventDefault(); + const old = m.must_change ? (sessionStorage.getItem('t66.old') || 'traffic66') : pf.old.value; + if (pf.new.value !== pf.again.value) return say('#pwMsg', false, t('acct.err_mismatch')); + const r = await fetch('/api/password', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({old, new: pf.new.value})}); + const j = await r.json().catch(() => ({})); + if (!r.ok) return say('#pwMsg', false, errText(j.error)); + sessionStorage.removeItem('t66.old'); + toast(t('acct.saved')); + if (m.must_change) { me.must_change = false; go('overview'); } else render(); + }; + const putUser = async (user, password) => { + const r = await fetch('/api/users', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({user, password})}); + const j = await r.json().catch(() => ({})); + if (!r.ok) { say('#userMsg', false, errText(j.error)); return false; } + toast(t('acct.added')); render(); return true; + }; + const au = $('#addUser'); + if (au) au.onsubmit = e => { e.preventDefault(); putUser(au.user.value.trim(), au.pw.value); }; + el.querySelectorAll('[data-reset]').forEach(b => b.onclick = () => { const row = el.querySelector(`[data-resetrow="${CSS.escape(b.dataset.reset)}"]`); row.hidden = !row.hidden; }); + el.querySelectorAll('[data-resetform]').forEach(f => f.onsubmit = e => { e.preventDefault(); putUser(f.dataset.resetform, f.pw.value); }); + el.querySelectorAll('[data-udel]').forEach(b => b.onclick = async () => { + if (!confirm(t('acct.confirm_del', {u: b.dataset.udel}))) return; + const r = await fetch('/api/users?user=' + encodeURIComponent(b.dataset.udel), {method: 'DELETE'}); + const j = await r.json().catch(() => ({})); + if (!r.ok) return say('#userMsg', false, errText(j.error)); + render(); + }); +}; + views.threats = async (el) => { const d = await api('threats', {limit: 66}); const rows = d.rows || [], lists = d.lists || {}; @@ -1585,7 +1650,7 @@ views.detail = async (el) => { // ------------------------------------------------------------ shell // Settings, data cleanup, offline analysis and live flow records have no use // for the time range, refresh and copy link. -const NOBAR_VIEWS = new Set(['sources', 'cleanup', 'sandbox']); +const NOBAR_VIEWS = new Set(['sources', 'cleanup', 'sandbox', 'account']); function renderBar() { const off = NOBAR_VIEWS.has(state.v) || (state.v === 'records' && state.rl); if (off) $('#range').hidden = true; @@ -1632,6 +1697,9 @@ async function loadSB() { } async function render(push) { + // the default password of a new installation is changed before anything else + if (me?.must_change) state.v = 'account'; + document.body.classList.toggle('mustchange', !!me?.must_change); renderSB(); renderBar(); renderFilters(); @@ -1687,14 +1755,28 @@ async function loadStatus() { function showLogin() { $('#app').hidden = true; $('#login').hidden = false; $('#loginForm [name=user]').focus(); + // the default password, while a new installation (or the demo) still has it + fetch('/api/loginhint').then(r => r.json()).then(h => { + const el = $('#loginHint'); + el.hidden = !h.default; + if (h.default) { el.textContent = t(h.demo ? 'login.hint_demo' : 'login.hint', {u: h.user, p: h.password}); if (!$('#loginForm [name=user]').value) $('#loginForm [name=user]').value = h.user; } + }).catch(() => {}); +} +// me: the signed-in user (see /api/me) +let me = null; +async function loadMe() { + try { const r = await fetch('/api/me'); if (r.ok) me = await r.json(); } catch (e) {} + $('#acctName').textContent = me?.user ? me.user : t('nav.account'); } $('#loginForm').addEventListener('submit', async e => { e.preventDefault(); const f = new FormData(e.target); const res = await fetch('/api/login', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({user: f.get('user'), password: f.get('password')})}); if (!res.ok) { $('#loginErr').hidden = false; $('#loginErr').textContent = t('login.bad'); return; } + const j = await res.json().catch(() => ({})); $('#login').hidden = true; $('#app').hidden = false; - await Promise.all([loadStatus(), loadSB()]); + if (j.must_change) sessionStorage.setItem('t66.old', f.get('password')); + await Promise.all([loadStatus(), loadSB(), loadMe()]); render(); startTimers(); }); @@ -1718,7 +1800,8 @@ function startTimers() { if (state.v === 'sources' || state.v === 'records' || state.v === 'overview' || state.v === 'findings' || state.r === '15m' || state.r === '1h') render(); }, 30000); } -$('#logout').onclick = async () => { await fetch('/api/logout', {method: 'POST'}); showLogin(); }; +$('#logout').onclick = async () => { await fetch('/api/logout', {method: 'POST'}); me = null; showLogin(); }; +$('#acct').onclick = () => go('account'); async function init() { $('#lang').innerHTML = LANGS.map(([c, n]) => ``).join(''); @@ -1747,7 +1830,7 @@ async function init() { const res = await fetch('/api/status'); if (res.status === 401) { showLogin(); return; } $('#app').hidden = false; - await Promise.all([loadStatus(), loadSB()]); + await Promise.all([loadStatus(), loadSB(), loadMe()]); render(); startTimers(); } diff --git a/internal/web/static/i18n/ar.json b/internal/web/static/i18n/ar.json index 70cb4cd..1392dce 100644 --- a/internal/web/static/i18n/ar.json +++ b/internal/web/static/i18n/ar.json @@ -515,5 +515,39 @@ "src.sampled_if": "العينات من: {v}", "src.dir_yes": "مع flowDirection (61)", "src.dir_no": "بدون flowDirection (61): الواجهة مستنتجة من الحركة", -"if.capture_only": "تقارن مطابقة الواجهات التدفقات بعدادات الواجهات في أجهزة sFlow وNetFlow وIPFIX. الالتقاط المحلي على بطاقة شبكة هذا الجهاز لا يحمل واجهات أو عدادات أجهزة، لذا لا يوجد ما يُقارن هنا." +"if.capture_only": "تقارن مطابقة الواجهات التدفقات بعدادات الواجهات في أجهزة sFlow وNetFlow وIPFIX. الالتقاط المحلي على بطاقة شبكة هذا الجهاز لا يحمل واجهات أو عدادات أجهزة، لذا لا يوجد ما يُقارن هنا.", +"nav.account": "الحساب", +"acct.title_pw": "تغيير كلمة المرور", +"acct.old": "كلمة المرور الحالية", +"acct.new": "كلمة المرور الجديدة", +"acct.confirm": "تأكيد كلمة المرور الجديدة", +"acct.save": "حفظ", +"acct.saved": "تم الحفظ", +"acct.must": "هذه كلمة المرور الافتراضية لتثبيت جديد. عيّن كلمة مرور جديدة قبل البدء.", +"acct.rule": "8 أحرف على الأقل، وليست traffic66.", +"acct.err_wrong_old": "كلمة المرور الحالية غير صحيحة.", +"acct.err_too_short": "يجب أن تتكون كلمة المرور الجديدة من 8 أحرف على الأقل.", +"acct.err_default": "لا يمكن أن تكون كلمة المرور الجديدة هي كلمة المرور الافتراضية traffic66.", +"acct.err_mismatch": "كلمتا المرور الجديدتان غير متطابقتين.", +"acct.err_bad_user": "لا يمكن أن يكون اسم المستخدم فارغًا أو يحتوي على مسافات أو نقطتين.", +"acct.err_self": "لا يمكنك حذف نفسك.", +"acct.err_fixed": "كلمة مرور هذا التشغيل حُددت عند البدء ولا يمكن تغييرها هنا.", +"acct.err_not_admin": "المسؤول وحده يدير المستخدمين.", +"acct.fixed": "يستخدم هذا التشغيل كلمة المرور المحددة عند البدء (-password أو TRAFFIC66_PASSWORD)؛ لا يمكن تغييرها هنا.", +"acct.users": "المستخدمون", +"acct.users_sub": "يرى جميع المستخدمين الأشياء نفسها ويمكنهم تغييرها؛ المسؤول وحده يدير المستخدمين", +"acct.user": "اسم المستخدم", +"acct.add": "إضافة مستخدم", +"acct.reset": "إعادة تعيين كلمة المرور", +"acct.delete": "حذف", +"acct.admin_tag": "المسؤول", +"acct.confirm_del": "حذف المستخدم {u}؟ سيتم تسجيل خروج متصفحاته المفتوحة.", +"acct.added": "تم الحفظ", +"acct.ldap": "تسجيل الدخول عبر LDAP / AD", +"acct.dev": "قيد التطوير", +"acct.ldap_dev": "قيد التطوير: تسجيل الدخول إلى traffic66 بحسابات LDAP أو Active Directory الخاصة بشركتك.", +"acct.ldap_server": "الخادم", +"acct.ldap_base": "Base DN", +"login.hint": "أول تسجيل دخول: المستخدم {u}، كلمة المرور {p}", +"login.hint_demo": "تجريبي: المستخدم {u}، كلمة المرور {p}" } diff --git a/internal/web/static/i18n/bn.json b/internal/web/static/i18n/bn.json index 3e94cfa..b575bc1 100644 --- a/internal/web/static/i18n/bn.json +++ b/internal/web/static/i18n/bn.json @@ -515,5 +515,39 @@ "src.sampled_if": "স্যাম্পল ইন্টারফেস: {v}", "src.dir_yes": "flowDirection (61) সহ", "src.dir_no": "flowDirection (61) ছাড়া: স্যাম্পল ইন্টারফেস ট্রাফিক থেকে অনুমান", -"if.capture_only": "ইন্টারফেস মিলান sFlow, NetFlow ও IPFIX ডিভাইসের ফ্লোকে ইন্টারফেস কাউন্টারের সঙ্গে মেলায়। এই মেশিনের নেটওয়ার্ক কার্ডে লোকাল ক্যাপচারে ডিভাইসের ইন্টারফেস বা কাউন্টার থাকে না, তাই এখানে মেলানোর কিছু নেই।" +"if.capture_only": "ইন্টারফেস মিলান sFlow, NetFlow ও IPFIX ডিভাইসের ফ্লোকে ইন্টারফেস কাউন্টারের সঙ্গে মেলায়। এই মেশিনের নেটওয়ার্ক কার্ডে লোকাল ক্যাপচারে ডিভাইসের ইন্টারফেস বা কাউন্টার থাকে না, তাই এখানে মেলানোর কিছু নেই।", +"nav.account": "অ্যাকাউন্ট", +"acct.title_pw": "পাসওয়ার্ড পরিবর্তন", +"acct.old": "বর্তমান পাসওয়ার্ড", +"acct.new": "নতুন পাসওয়ার্ড", +"acct.confirm": "নতুন পাসওয়ার্ড আবার", +"acct.save": "সংরক্ষণ", +"acct.saved": "সংরক্ষিত হয়েছে", +"acct.must": "এটি নতুন ইনস্টলেশনের ডিফল্ট পাসওয়ার্ড। শুরু করার আগে একটি নতুন পাসওয়ার্ড সেট করুন।", +"acct.rule": "কমপক্ষে 8 অক্ষর, traffic66 নয়।", +"acct.err_wrong_old": "বর্তমান পাসওয়ার্ড ভুল।", +"acct.err_too_short": "নতুন পাসওয়ার্ডে কমপক্ষে 8 অক্ষর লাগবে।", +"acct.err_default": "নতুন পাসওয়ার্ড ডিফল্ট পাসওয়ার্ড traffic66 হতে পারে না।", +"acct.err_mismatch": "দুটি নতুন পাসওয়ার্ড মেলেনি।", +"acct.err_bad_user": "ব্যবহারকারীর নাম খালি হতে পারে না এবং এতে স্পেস বা কোলন থাকতে পারে না।", +"acct.err_self": "আপনি নিজেকে মুছতে পারবেন না।", +"acct.err_fixed": "এই রানের পাসওয়ার্ড শুরুতে দেওয়া হয়েছে, এখানে পরিবর্তন করা যাবে না।", +"acct.err_not_admin": "শুধু প্রশাসক ব্যবহারকারী পরিচালনা করেন।", +"acct.fixed": "এই রান শুরুতে দেওয়া পাসওয়ার্ড (-password বা TRAFFIC66_PASSWORD) ব্যবহার করে; এখানে পরিবর্তন করা যাবে না।", +"acct.users": "ব্যবহারকারী", +"acct.users_sub": "সব ব্যবহারকারী একই জিনিস দেখেন ও পরিবর্তন করতে পারেন; শুধু প্রশাসক ব্যবহারকারী পরিচালনা করেন", +"acct.user": "ব্যবহারকারীর নাম", +"acct.add": "ব্যবহারকারী যোগ করুন", +"acct.reset": "পাসওয়ার্ড রিসেট", +"acct.delete": "মুছুন", +"acct.admin_tag": "প্রশাসক", +"acct.confirm_del": "ব্যবহারকারী {u} মুছবেন? তাঁর খোলা ব্রাউজারগুলো সাইন আউট হবে।", +"acct.added": "সংরক্ষিত হয়েছে", +"acct.ldap": "LDAP / AD সাইন ইন", +"acct.dev": "উন্নয়নাধীন", +"acct.ldap_dev": "উন্নয়নাধীন: আপনার প্রতিষ্ঠানের LDAP বা Active Directory অ্যাকাউন্ট দিয়ে traffic66-এ সাইন ইন।", +"acct.ldap_server": "সার্ভার", +"acct.ldap_base": "Base DN", +"login.hint": "প্রথম সাইন ইন: ব্যবহারকারী {u}, পাসওয়ার্ড {p}", +"login.hint_demo": "ডেমো: ব্যবহারকারী {u}, পাসওয়ার্ড {p}" } diff --git a/internal/web/static/i18n/en.json b/internal/web/static/i18n/en.json index dcee5b2..5438064 100644 --- a/internal/web/static/i18n/en.json +++ b/internal/web/static/i18n/en.json @@ -515,5 +515,39 @@ "src.sampled_if": "Sampled on: {v}", "src.dir_yes": "with flowDirection (61)", "src.dir_no": "without flowDirection (61): sampled interface inferred from traffic", -"if.capture_only": "Interface check compares flows with the interface counters of sFlow, NetFlow and IPFIX devices. Local capture on this machine's network card has no device interfaces or counters, so there is nothing to compare here." +"if.capture_only": "Interface check compares flows with the interface counters of sFlow, NetFlow and IPFIX devices. Local capture on this machine's network card has no device interfaces or counters, so there is nothing to compare here.", +"nav.account": "Account", +"acct.title_pw": "Change password", +"acct.old": "Current password", +"acct.new": "New password", +"acct.confirm": "New password again", +"acct.save": "Save", +"acct.saved": "Saved", +"acct.must": "This is the default password of a new installation. Set a new password before you start.", +"acct.rule": "At least 8 characters, not traffic66.", +"acct.err_wrong_old": "The current password is wrong.", +"acct.err_too_short": "The new password needs at least 8 characters.", +"acct.err_default": "The new password cannot be the default password traffic66.", +"acct.err_mismatch": "The two new passwords differ.", +"acct.err_bad_user": "A user name cannot be empty or contain spaces or colons.", +"acct.err_self": "You cannot delete yourself.", +"acct.err_fixed": "This run's password was given at start and cannot be changed here.", +"acct.err_not_admin": "Only the administrator manages users.", +"acct.fixed": "This run uses the password given at start (-password or TRAFFIC66_PASSWORD); it cannot be changed here.", +"acct.users": "Users", +"acct.users_sub": "Every user sees and can change the same things; only the administrator manages users", +"acct.user": "User name", +"acct.add": "Add user", +"acct.reset": "Reset password", +"acct.delete": "Delete", +"acct.admin_tag": "administrator", +"acct.confirm_del": "Delete the user {u}? Their open browsers are signed out.", +"acct.added": "Saved", +"acct.ldap": "LDAP / AD sign-in", +"acct.dev": "In development", +"acct.ldap_dev": "In development: signing in to traffic66 with your company's LDAP or Active Directory accounts.", +"acct.ldap_server": "Server", +"acct.ldap_base": "Base DN", +"login.hint": "First sign-in: user {u}, password {p}", +"login.hint_demo": "Demo: user {u}, password {p}" } diff --git a/internal/web/static/i18n/es.json b/internal/web/static/i18n/es.json index c265b6a..b5ccdd2 100644 --- a/internal/web/static/i18n/es.json +++ b/internal/web/static/i18n/es.json @@ -515,5 +515,39 @@ "src.sampled_if": "Muestreo en: {v}", "src.dir_yes": "con flowDirection (61)", "src.dir_no": "sin flowDirection (61): interfaz muestreada deducida del tráfico", -"if.capture_only": "La verificación de interfaces compara los flujos con los contadores de interfaz de equipos sFlow, NetFlow e IPFIX. La captura local en la tarjeta de red de este equipo no tiene interfaces ni contadores de dispositivo, así que aquí no hay nada que comparar." +"if.capture_only": "La verificación de interfaces compara los flujos con los contadores de interfaz de equipos sFlow, NetFlow e IPFIX. La captura local en la tarjeta de red de este equipo no tiene interfaces ni contadores de dispositivo, así que aquí no hay nada que comparar.", +"nav.account": "Cuenta", +"acct.title_pw": "Cambiar contraseña", +"acct.old": "Contraseña actual", +"acct.new": "Nueva contraseña", +"acct.confirm": "Repetir nueva contraseña", +"acct.save": "Guardar", +"acct.saved": "Guardado", +"acct.must": "Es la contraseña predeterminada de una instalación nueva. Establezca una contraseña nueva antes de empezar.", +"acct.rule": "Al menos 8 caracteres, no traffic66.", +"acct.err_wrong_old": "La contraseña actual es incorrecta.", +"acct.err_too_short": "La nueva contraseña necesita al menos 8 caracteres.", +"acct.err_default": "La nueva contraseña no puede ser la predeterminada traffic66.", +"acct.err_mismatch": "Las dos contraseñas nuevas no coinciden.", +"acct.err_bad_user": "El nombre de usuario no puede estar vacío ni contener espacios o dos puntos.", +"acct.err_self": "No puede eliminarse a sí mismo.", +"acct.err_fixed": "La contraseña de esta ejecución se indicó al iniciar y no se puede cambiar aquí.", +"acct.err_not_admin": "Solo el administrador gestiona usuarios.", +"acct.fixed": "Esta ejecución usa la contraseña indicada al iniciar (-password o TRAFFIC66_PASSWORD); no se puede cambiar aquí.", +"acct.users": "Usuarios", +"acct.users_sub": "Todos los usuarios ven y pueden cambiar lo mismo; solo el administrador gestiona usuarios", +"acct.user": "Nombre de usuario", +"acct.add": "Añadir usuario", +"acct.reset": "Restablecer contraseña", +"acct.delete": "Eliminar", +"acct.admin_tag": "administrador", +"acct.confirm_del": "¿Eliminar el usuario {u}? Se cerrará la sesión en sus navegadores abiertos.", +"acct.added": "Guardado", +"acct.ldap": "Inicio de sesión LDAP / AD", +"acct.dev": "En desarrollo", +"acct.ldap_dev": "En desarrollo: iniciar sesión en traffic66 con las cuentas LDAP o Active Directory de su empresa.", +"acct.ldap_server": "Servidor", +"acct.ldap_base": "Base DN", +"login.hint": "Primer inicio de sesión: usuario {u}, contraseña {p}", +"login.hint_demo": "Demo: usuario {u}, contraseña {p}" } diff --git a/internal/web/static/i18n/fr.json b/internal/web/static/i18n/fr.json index abf3f22..fb0f261 100644 --- a/internal/web/static/i18n/fr.json +++ b/internal/web/static/i18n/fr.json @@ -515,5 +515,39 @@ "src.sampled_if": "Échantillonné sur : {v}", "src.dir_yes": "avec flowDirection (61)", "src.dir_no": "sans flowDirection (61) : interface échantillonnée déduite du trafic", -"if.capture_only": "Le contrôle des interfaces compare les flux aux compteurs d’interface des équipements sFlow, NetFlow et IPFIX. La capture locale sur la carte réseau de cette machine n’a ni interfaces ni compteurs d’équipement : il n’y a rien à comparer ici." +"if.capture_only": "Le contrôle des interfaces compare les flux aux compteurs d’interface des équipements sFlow, NetFlow et IPFIX. La capture locale sur la carte réseau de cette machine n’a ni interfaces ni compteurs d’équipement : il n’y a rien à comparer ici.", +"nav.account": "Compte", +"acct.title_pw": "Changer le mot de passe", +"acct.old": "Mot de passe actuel", +"acct.new": "Nouveau mot de passe", +"acct.confirm": "Confirmer le nouveau mot de passe", +"acct.save": "Enregistrer", +"acct.saved": "Enregistré", +"acct.must": "Ceci est le mot de passe par défaut d'une nouvelle installation. Définissez un nouveau mot de passe avant de commencer.", +"acct.rule": "Au moins 8 caractères, pas traffic66.", +"acct.err_wrong_old": "Le mot de passe actuel est incorrect.", +"acct.err_too_short": "Le nouveau mot de passe doit contenir au moins 8 caractères.", +"acct.err_default": "Le nouveau mot de passe ne peut pas être le mot de passe par défaut traffic66.", +"acct.err_mismatch": "Les deux nouveaux mots de passe diffèrent.", +"acct.err_bad_user": "Le nom d'utilisateur ne peut pas être vide ni contenir d'espaces ou de deux-points.", +"acct.err_self": "Vous ne pouvez pas vous supprimer vous-même.", +"acct.err_fixed": "Le mot de passe de cette exécution a été fourni au démarrage et ne peut pas être changé ici.", +"acct.err_not_admin": "Seul l'administrateur gère les utilisateurs.", +"acct.fixed": "Cette exécution utilise le mot de passe fourni au démarrage (-password ou TRAFFIC66_PASSWORD) ; il ne peut pas être changé ici.", +"acct.users": "Utilisateurs", +"acct.users_sub": "Tous les utilisateurs voient et peuvent modifier les mêmes choses ; seul l'administrateur gère les utilisateurs", +"acct.user": "Nom d'utilisateur", +"acct.add": "Ajouter un utilisateur", +"acct.reset": "Réinitialiser le mot de passe", +"acct.delete": "Supprimer", +"acct.admin_tag": "administrateur", +"acct.confirm_del": "Supprimer l'utilisateur {u} ? Ses navigateurs ouverts seront déconnectés.", +"acct.added": "Enregistré", +"acct.ldap": "Connexion LDAP / AD", +"acct.dev": "En développement", +"acct.ldap_dev": "En développement : connexion à traffic66 avec les comptes LDAP ou Active Directory de votre entreprise.", +"acct.ldap_server": "Serveur", +"acct.ldap_base": "Base DN", +"login.hint": "Première connexion : utilisateur {u}, mot de passe {p}", +"login.hint_demo": "Démo : utilisateur {u}, mot de passe {p}" } diff --git a/internal/web/static/i18n/hi.json b/internal/web/static/i18n/hi.json index 0696fa9..8867e6d 100644 --- a/internal/web/static/i18n/hi.json +++ b/internal/web/static/i18n/hi.json @@ -515,5 +515,39 @@ "src.sampled_if": "सैंपल इंटरफ़ेस: {v}", "src.dir_yes": "flowDirection (61) सहित", "src.dir_no": "flowDirection (61) के बिना: सैंपल इंटरफ़ेस ट्रैफ़िक से अनुमानित", -"if.capture_only": "इंटरफ़ेस मिलान sFlow, NetFlow और IPFIX डिवाइसों के फ़्लो की तुलना इंटरफ़ेस काउंटरों से करता है। इस मशीन के नेटवर्क कार्ड पर लोकल कैप्चर में डिवाइस इंटरफ़ेस या काउंटर नहीं होते, इसलिए यहाँ तुलना के लिए कुछ नहीं है।" +"if.capture_only": "इंटरफ़ेस मिलान sFlow, NetFlow और IPFIX डिवाइसों के फ़्लो की तुलना इंटरफ़ेस काउंटरों से करता है। इस मशीन के नेटवर्क कार्ड पर लोकल कैप्चर में डिवाइस इंटरफ़ेस या काउंटर नहीं होते, इसलिए यहाँ तुलना के लिए कुछ नहीं है।", +"nav.account": "खाता", +"acct.title_pw": "पासवर्ड बदलें", +"acct.old": "वर्तमान पासवर्ड", +"acct.new": "नया पासवर्ड", +"acct.confirm": "नया पासवर्ड फिर से", +"acct.save": "सहेजें", +"acct.saved": "सहेज लिया गया", +"acct.must": "यह नई स्थापना का डिफ़ॉल्ट पासवर्ड है। शुरू करने से पहले नया पासवर्ड सेट करें।", +"acct.rule": "कम से कम 8 अक्षर, traffic66 नहीं।", +"acct.err_wrong_old": "वर्तमान पासवर्ड गलत है।", +"acct.err_too_short": "नए पासवर्ड में कम से कम 8 अक्षर होने चाहिए।", +"acct.err_default": "नया पासवर्ड डिफ़ॉल्ट पासवर्ड traffic66 नहीं हो सकता।", +"acct.err_mismatch": "दोनों नए पासवर्ड अलग हैं।", +"acct.err_bad_user": "उपयोगकर्ता नाम खाली नहीं हो सकता, न ही उसमें स्पेस या कोलन हो सकते हैं।", +"acct.err_self": "आप स्वयं को नहीं हटा सकते।", +"acct.err_fixed": "इस रन का पासवर्ड शुरू में दिया गया था और यहाँ बदला नहीं जा सकता।", +"acct.err_not_admin": "केवल व्यवस्थापक उपयोगकर्ताओं का प्रबंधन करता है।", +"acct.fixed": "यह रन शुरू में दिए गए पासवर्ड (-password या TRAFFIC66_PASSWORD) का उपयोग करता है; इसे यहाँ बदला नहीं जा सकता।", +"acct.users": "उपयोगकर्ता", +"acct.users_sub": "सभी उपयोगकर्ता एक जैसी चीज़ें देखते और बदल सकते हैं; केवल व्यवस्थापक उपयोगकर्ताओं का प्रबंधन करता है", +"acct.user": "उपयोगकर्ता नाम", +"acct.add": "उपयोगकर्ता जोड़ें", +"acct.reset": "पासवर्ड रीसेट करें", +"acct.delete": "हटाएँ", +"acct.admin_tag": "व्यवस्थापक", +"acct.confirm_del": "उपयोगकर्ता {u} को हटाएँ? उनके खुले ब्राउज़र साइन आउट हो जाएँगे।", +"acct.added": "सहेज लिया गया", +"acct.ldap": "LDAP / AD साइन इन", +"acct.dev": "विकास में", +"acct.ldap_dev": "विकास में: अपनी कंपनी के LDAP या Active Directory खातों से traffic66 में साइन इन।", +"acct.ldap_server": "सर्वर", +"acct.ldap_base": "Base DN", +"login.hint": "पहला साइन इन: उपयोगकर्ता {u}, पासवर्ड {p}", +"login.hint_demo": "डेमो: उपयोगकर्ता {u}, पासवर्ड {p}" } diff --git a/internal/web/static/i18n/id.json b/internal/web/static/i18n/id.json index 62d816b..a1c6d75 100644 --- a/internal/web/static/i18n/id.json +++ b/internal/web/static/i18n/id.json @@ -515,5 +515,39 @@ "src.sampled_if": "Disampel di: {v}", "src.dir_yes": "dengan flowDirection (61)", "src.dir_no": "tanpa flowDirection (61): antarmuka yang disampel ditebak dari trafik", -"if.capture_only": "Pencocokan antarmuka membandingkan aliran dengan penghitung antarmuka perangkat sFlow, NetFlow, dan IPFIX. Tangkapan lokal di kartu jaringan mesin ini tidak punya antarmuka atau penghitung perangkat, jadi tidak ada yang dibandingkan di sini." +"if.capture_only": "Pencocokan antarmuka membandingkan aliran dengan penghitung antarmuka perangkat sFlow, NetFlow, dan IPFIX. Tangkapan lokal di kartu jaringan mesin ini tidak punya antarmuka atau penghitung perangkat, jadi tidak ada yang dibandingkan di sini.", +"nav.account": "Akun", +"acct.title_pw": "Ubah kata sandi", +"acct.old": "Kata sandi saat ini", +"acct.new": "Kata sandi baru", +"acct.confirm": "Ulangi kata sandi baru", +"acct.save": "Simpan", +"acct.saved": "Tersimpan", +"acct.must": "Ini adalah kata sandi bawaan instalasi baru. Tetapkan kata sandi baru sebelum mulai.", +"acct.rule": "Minimal 8 karakter, bukan traffic66.", +"acct.err_wrong_old": "Kata sandi saat ini salah.", +"acct.err_too_short": "Kata sandi baru minimal 8 karakter.", +"acct.err_default": "Kata sandi baru tidak boleh kata sandi bawaan traffic66.", +"acct.err_mismatch": "Kedua kata sandi baru berbeda.", +"acct.err_bad_user": "Nama pengguna tidak boleh kosong atau berisi spasi atau titik dua.", +"acct.err_self": "Anda tidak dapat menghapus diri sendiri.", +"acct.err_fixed": "Kata sandi proses ini diberikan saat mulai dan tidak dapat diubah di sini.", +"acct.err_not_admin": "Hanya administrator yang mengelola pengguna.", +"acct.fixed": "Proses ini memakai kata sandi yang diberikan saat mulai (-password atau TRAFFIC66_PASSWORD); tidak dapat diubah di sini.", +"acct.users": "Pengguna", +"acct.users_sub": "Semua pengguna melihat dan dapat mengubah hal yang sama; hanya administrator yang mengelola pengguna", +"acct.user": "Nama pengguna", +"acct.add": "Tambah pengguna", +"acct.reset": "Atur ulang kata sandi", +"acct.delete": "Hapus", +"acct.admin_tag": "administrator", +"acct.confirm_del": "Hapus pengguna {u}? Browser yang terbuka akan dikeluarkan.", +"acct.added": "Tersimpan", +"acct.ldap": "Masuk LDAP / AD", +"acct.dev": "Dalam pengembangan", +"acct.ldap_dev": "Dalam pengembangan: masuk ke traffic66 dengan akun LDAP atau Active Directory perusahaan Anda.", +"acct.ldap_server": "Server", +"acct.ldap_base": "Base DN", +"login.hint": "Masuk pertama: pengguna {u}, kata sandi {p}", +"login.hint_demo": "Demo: pengguna {u}, kata sandi {p}" } diff --git a/internal/web/static/i18n/ja.json b/internal/web/static/i18n/ja.json index 5db1f1c..5f12e08 100644 --- a/internal/web/static/i18n/ja.json +++ b/internal/web/static/i18n/ja.json @@ -515,5 +515,39 @@ "src.sampled_if": "サンプリング対象:{v}", "src.dir_yes": "方向フィールド(61)あり", "src.dir_no": "方向フィールド(61)なし:サンプリング対象はトラフィックから推定", -"if.capture_only": "インターフェース照合は sFlow・NetFlow・IPFIX 機器のフローとインターフェースカウンターを比べます。このマシンの NIC でのローカルキャプチャには機器のインターフェースもカウンターもないため、ここで比べるものはありません。" +"if.capture_only": "インターフェース照合は sFlow・NetFlow・IPFIX 機器のフローとインターフェースカウンターを比べます。このマシンの NIC でのローカルキャプチャには機器のインターフェースもカウンターもないため、ここで比べるものはありません。", +"nav.account": "アカウント", +"acct.title_pw": "パスワード変更", +"acct.old": "現在のパスワード", +"acct.new": "新しいパスワード", +"acct.confirm": "新しいパスワード(確認)", +"acct.save": "保存", +"acct.saved": "保存しました", +"acct.must": "新規インストール時の初期パスワードです。使い始める前に新しいパスワードを設定してください。", +"acct.rule": "8文字以上、traffic66 以外。", +"acct.err_wrong_old": "現在のパスワードが違います。", +"acct.err_too_short": "新しいパスワードは8文字以上必要です。", +"acct.err_default": "新しいパスワードを初期パスワード traffic66 にはできません。", +"acct.err_mismatch": "新しいパスワードが一致しません。", +"acct.err_bad_user": "ユーザー名は空にできず、空白やコロンを含められません。", +"acct.err_self": "自分自身は削除できません。", +"acct.err_fixed": "このプロセスのパスワードは起動時に指定されたため、ここでは変更できません。", +"acct.err_not_admin": "ユーザー管理は管理者のみ行えます。", +"acct.fixed": "このプロセスは起動時に指定されたパスワード(-password または TRAFFIC66_PASSWORD)を使っています。ここでは変更できません。", +"acct.users": "ユーザー", +"acct.users_sub": "全ユーザーが同じ内容を閲覧・変更できます。ユーザー管理は管理者のみ", +"acct.user": "ユーザー名", +"acct.add": "ユーザー追加", +"acct.reset": "パスワードをリセット", +"acct.delete": "削除", +"acct.admin_tag": "管理者", +"acct.confirm_del": "ユーザー {u} を削除しますか?開いているブラウザはログアウトされます。", +"acct.added": "保存しました", +"acct.ldap": "LDAP / AD ログイン", +"acct.dev": "開発中", +"acct.ldap_dev": "開発中:社内の LDAP または Active Directory アカウントで traffic66 にログインできるようにします。", +"acct.ldap_server": "サーバー", +"acct.ldap_base": "Base DN", +"login.hint": "初回ログイン:ユーザー {u}、パスワード {p}", +"login.hint_demo": "デモ:ユーザー {u}、パスワード {p}" } diff --git a/internal/web/static/i18n/ko.json b/internal/web/static/i18n/ko.json index d79a0f7..a21df0e 100644 --- a/internal/web/static/i18n/ko.json +++ b/internal/web/static/i18n/ko.json @@ -515,5 +515,39 @@ "src.sampled_if": "샘플링 인터페이스: {v}", "src.dir_yes": "방향 필드(61) 있음", "src.dir_no": "방향 필드(61) 없음: 샘플링 인터페이스를 트래픽으로 추정", -"if.capture_only": "인터페이스 대조는 sFlow, NetFlow, IPFIX 장비의 플로와 인터페이스 카운터를 비교합니다. 이 컴퓨터 네트워크 카드의 로컬 캡처에는 장비 인터페이스와 카운터가 없어 여기서 비교할 것이 없습니다." +"if.capture_only": "인터페이스 대조는 sFlow, NetFlow, IPFIX 장비의 플로와 인터페이스 카운터를 비교합니다. 이 컴퓨터 네트워크 카드의 로컬 캡처에는 장비 인터페이스와 카운터가 없어 여기서 비교할 것이 없습니다.", +"nav.account": "계정", +"acct.title_pw": "비밀번호 변경", +"acct.old": "현재 비밀번호", +"acct.new": "새 비밀번호", +"acct.confirm": "새 비밀번호 확인", +"acct.save": "저장", +"acct.saved": "저장했습니다", +"acct.must": "새로 설치한 기본 비밀번호입니다. 사용하기 전에 새 비밀번호를 설정하세요.", +"acct.rule": "8자 이상, traffic66 제외.", +"acct.err_wrong_old": "현재 비밀번호가 틀렸습니다.", +"acct.err_too_short": "새 비밀번호는 8자 이상이어야 합니다.", +"acct.err_default": "새 비밀번호를 기본 비밀번호 traffic66으로 할 수 없습니다.", +"acct.err_mismatch": "두 새 비밀번호가 다릅니다.", +"acct.err_bad_user": "사용자 이름은 비워 둘 수 없고 공백이나 콜론을 포함할 수 없습니다.", +"acct.err_self": "자기 자신은 삭제할 수 없습니다.", +"acct.err_fixed": "이번 실행의 비밀번호는 시작 시 지정되어 여기서 변경할 수 없습니다.", +"acct.err_not_admin": "사용자 관리는 관리자만 할 수 있습니다.", +"acct.fixed": "이번 실행은 시작 시 지정된 비밀번호(-password 또는 TRAFFIC66_PASSWORD)를 사용합니다. 여기서 변경할 수 없습니다.", +"acct.users": "사용자", +"acct.users_sub": "모든 사용자가 같은 내용을 보고 변경할 수 있으며, 사용자 관리는 관리자만 가능합니다", +"acct.user": "사용자 이름", +"acct.add": "사용자 추가", +"acct.reset": "비밀번호 재설정", +"acct.delete": "삭제", +"acct.admin_tag": "관리자", +"acct.confirm_del": "사용자 {u}을(를) 삭제할까요? 열려 있는 브라우저는 로그아웃됩니다.", +"acct.added": "저장했습니다", +"acct.ldap": "LDAP / AD 로그인", +"acct.dev": "개발 중", +"acct.ldap_dev": "개발 중: 회사의 LDAP 또는 Active Directory 계정으로 traffic66에 로그인합니다.", +"acct.ldap_server": "서버", +"acct.ldap_base": "Base DN", +"login.hint": "첫 로그인: 사용자 {u}, 비밀번호 {p}", +"login.hint_demo": "데모: 사용자 {u}, 비밀번호 {p}" } diff --git a/internal/web/static/i18n/pt.json b/internal/web/static/i18n/pt.json index 2ef9731..12ef0d4 100644 --- a/internal/web/static/i18n/pt.json +++ b/internal/web/static/i18n/pt.json @@ -515,5 +515,39 @@ "src.sampled_if": "Amostragem em: {v}", "src.dir_yes": "com flowDirection (61)", "src.dir_no": "sem flowDirection (61): interface amostrada deduzida do tráfego", -"if.capture_only": "A conferência de interfaces compara os fluxos com os contadores de interface de equipamentos sFlow, NetFlow e IPFIX. A captura local na placa de rede desta máquina não tem interfaces nem contadores de equipamento, então não há o que comparar aqui." +"if.capture_only": "A conferência de interfaces compara os fluxos com os contadores de interface de equipamentos sFlow, NetFlow e IPFIX. A captura local na placa de rede desta máquina não tem interfaces nem contadores de equipamento, então não há o que comparar aqui.", +"nav.account": "Conta", +"acct.title_pw": "Alterar senha", +"acct.old": "Senha atual", +"acct.new": "Nova senha", +"acct.confirm": "Repita a nova senha", +"acct.save": "Salvar", +"acct.saved": "Salvo", +"acct.must": "Esta é a senha padrão de uma nova instalação. Defina uma nova senha antes de começar.", +"acct.rule": "Pelo menos 8 caracteres, não traffic66.", +"acct.err_wrong_old": "A senha atual está errada.", +"acct.err_too_short": "A nova senha precisa de pelo menos 8 caracteres.", +"acct.err_default": "A nova senha não pode ser a senha padrão traffic66.", +"acct.err_mismatch": "As duas novas senhas são diferentes.", +"acct.err_bad_user": "O nome de usuário não pode estar vazio nem conter espaços ou dois-pontos.", +"acct.err_self": "Você não pode excluir a si mesmo.", +"acct.err_fixed": "A senha desta execução foi definida na inicialização e não pode ser alterada aqui.", +"acct.err_not_admin": "Somente o administrador gerencia usuários.", +"acct.fixed": "Esta execução usa a senha definida na inicialização (-password ou TRAFFIC66_PASSWORD); ela não pode ser alterada aqui.", +"acct.users": "Usuários", +"acct.users_sub": "Todos os usuários veem e podem alterar as mesmas coisas; somente o administrador gerencia usuários", +"acct.user": "Nome de usuário", +"acct.add": "Adicionar usuário", +"acct.reset": "Redefinir senha", +"acct.delete": "Excluir", +"acct.admin_tag": "administrador", +"acct.confirm_del": "Excluir o usuário {u}? Os navegadores abertos dele serão desconectados.", +"acct.added": "Salvo", +"acct.ldap": "Login LDAP / AD", +"acct.dev": "Em desenvolvimento", +"acct.ldap_dev": "Em desenvolvimento: entrar no traffic66 com as contas LDAP ou Active Directory da sua empresa.", +"acct.ldap_server": "Servidor", +"acct.ldap_base": "Base DN", +"login.hint": "Primeiro acesso: usuário {u}, senha {p}", +"login.hint_demo": "Demo: usuário {u}, senha {p}" } diff --git a/internal/web/static/i18n/ru.json b/internal/web/static/i18n/ru.json index bca26c4..02a6eee 100644 --- a/internal/web/static/i18n/ru.json +++ b/internal/web/static/i18n/ru.json @@ -515,5 +515,39 @@ "src.sampled_if": "Выборка на: {v}", "src.dir_yes": "с flowDirection (61)", "src.dir_no": "без flowDirection (61): интерфейс с выборкой определён по трафику", -"if.capture_only": "Сверка интерфейсов сравнивает потоки со счётчиками интерфейсов устройств sFlow, NetFlow и IPFIX. У локального захвата с сетевой карты этой машины нет интерфейсов и счётчиков устройства, поэтому сравнивать здесь нечего." +"if.capture_only": "Сверка интерфейсов сравнивает потоки со счётчиками интерфейсов устройств sFlow, NetFlow и IPFIX. У локального захвата с сетевой карты этой машины нет интерфейсов и счётчиков устройства, поэтому сравнивать здесь нечего.", +"nav.account": "Учётная запись", +"acct.title_pw": "Сменить пароль", +"acct.old": "Текущий пароль", +"acct.new": "Новый пароль", +"acct.confirm": "Повторите новый пароль", +"acct.save": "Сохранить", +"acct.saved": "Сохранено", +"acct.must": "Это пароль по умолчанию новой установки. Задайте новый пароль перед началом работы.", +"acct.rule": "Не менее 8 символов, не traffic66.", +"acct.err_wrong_old": "Неверный текущий пароль.", +"acct.err_too_short": "Новый пароль должен содержать не менее 8 символов.", +"acct.err_default": "Новый пароль не может быть паролем по умолчанию traffic66.", +"acct.err_mismatch": "Новые пароли не совпадают.", +"acct.err_bad_user": "Имя пользователя не может быть пустым или содержать пробелы и двоеточия.", +"acct.err_self": "Нельзя удалить самого себя.", +"acct.err_fixed": "Пароль этого запуска задан при старте и не может быть изменён здесь.", +"acct.err_not_admin": "Пользователями управляет только администратор.", +"acct.fixed": "Этот запуск использует пароль, заданный при старте (-password или TRAFFIC66_PASSWORD); изменить его здесь нельзя.", +"acct.users": "Пользователи", +"acct.users_sub": "Все пользователи видят и могут менять одно и то же; пользователями управляет только администратор", +"acct.user": "Имя пользователя", +"acct.add": "Добавить пользователя", +"acct.reset": "Сбросить пароль", +"acct.delete": "Удалить", +"acct.admin_tag": "администратор", +"acct.confirm_del": "Удалить пользователя {u}? Его открытые браузеры будут разлогинены.", +"acct.added": "Сохранено", +"acct.ldap": "Вход через LDAP / AD", +"acct.dev": "В разработке", +"acct.ldap_dev": "В разработке: вход в traffic66 с учётными записями LDAP или Active Directory вашей компании.", +"acct.ldap_server": "Сервер", +"acct.ldap_base": "Base DN", +"login.hint": "Первый вход: пользователь {u}, пароль {p}", +"login.hint_demo": "Демо: пользователь {u}, пароль {p}" } diff --git a/internal/web/static/i18n/ur.json b/internal/web/static/i18n/ur.json index 8426102..a66e387 100644 --- a/internal/web/static/i18n/ur.json +++ b/internal/web/static/i18n/ur.json @@ -515,5 +515,39 @@ "src.sampled_if": "سیمپل انٹرفیس: {v}", "src.dir_yes": "flowDirection (61) کے ساتھ", "src.dir_no": "flowDirection (61) کے بغیر: سیمپل انٹرفیس ٹریفک سے اخذ", -"if.capture_only": "انٹرفیس ملان sFlow، NetFlow اور IPFIX آلات کے فلو کا انٹرفیس کاؤنٹرز سے موازنہ کرتا ہے۔ اس مشین کے نیٹ ورک کارڈ پر لوکل کیپچر میں آلے کے انٹرفیس یا کاؤنٹر نہیں ہوتے، اس لیے یہاں موازنے کو کچھ نہیں۔" +"if.capture_only": "انٹرفیس ملان sFlow، NetFlow اور IPFIX آلات کے فلو کا انٹرفیس کاؤنٹرز سے موازنہ کرتا ہے۔ اس مشین کے نیٹ ورک کارڈ پر لوکل کیپچر میں آلے کے انٹرفیس یا کاؤنٹر نہیں ہوتے، اس لیے یہاں موازنے کو کچھ نہیں۔", +"nav.account": "اکاؤنٹ", +"acct.title_pw": "پاس ورڈ تبدیل کریں", +"acct.old": "موجودہ پاس ورڈ", +"acct.new": "نیا پاس ورڈ", +"acct.confirm": "نیا پاس ورڈ دوبارہ", +"acct.save": "محفوظ کریں", +"acct.saved": "محفوظ ہو گیا", +"acct.must": "یہ نئی انسٹالیشن کا ڈیفالٹ پاس ورڈ ہے۔ شروع کرنے سے پہلے نیا پاس ورڈ سیٹ کریں۔", +"acct.rule": "کم از کم 8 حروف، traffic66 نہیں۔", +"acct.err_wrong_old": "موجودہ پاس ورڈ غلط ہے۔", +"acct.err_too_short": "نئے پاس ورڈ میں کم از کم 8 حروف ہونے چاہئیں۔", +"acct.err_default": "نیا پاس ورڈ ڈیفالٹ پاس ورڈ traffic66 نہیں ہو سکتا۔", +"acct.err_mismatch": "دونوں نئے پاس ورڈ مختلف ہیں۔", +"acct.err_bad_user": "صارف نام خالی نہیں ہو سکتا اور اس میں اسپیس یا کولن نہیں ہو سکتے۔", +"acct.err_self": "آپ خود کو حذف نہیں کر سکتے۔", +"acct.err_fixed": "اس رن کا پاس ورڈ شروع میں دیا گیا تھا اور یہاں تبدیل نہیں ہو سکتا۔", +"acct.err_not_admin": "صرف منتظم صارفین کا انتظام کرتا ہے۔", +"acct.fixed": "یہ رن شروع میں دیا گیا پاس ورڈ (-password یا TRAFFIC66_PASSWORD) استعمال کرتا ہے؛ اسے یہاں تبدیل نہیں کیا جا سکتا۔", +"acct.users": "صارفین", +"acct.users_sub": "تمام صارفین ایک جیسی چیزیں دیکھتے اور تبدیل کر سکتے ہیں؛ صرف منتظم صارفین کا انتظام کرتا ہے", +"acct.user": "صارف نام", +"acct.add": "صارف شامل کریں", +"acct.reset": "پاس ورڈ ری سیٹ کریں", +"acct.delete": "حذف کریں", +"acct.admin_tag": "منتظم", +"acct.confirm_del": "صارف {u} کو حذف کریں؟ اس کے کھلے براؤزر سائن آؤٹ ہو جائیں گے۔", +"acct.added": "محفوظ ہو گیا", +"acct.ldap": "LDAP / AD سائن ان", +"acct.dev": "زیرِ تیاری", +"acct.ldap_dev": "زیرِ تیاری: اپنی کمپنی کے LDAP یا Active Directory اکاؤنٹس سے traffic66 میں سائن ان۔", +"acct.ldap_server": "سرور", +"acct.ldap_base": "Base DN", +"login.hint": "پہلا سائن ان: صارف {u}، پاس ورڈ {p}", +"login.hint_demo": "ڈیمو: صارف {u}، پاس ورڈ {p}" } diff --git a/internal/web/static/i18n/zh.json b/internal/web/static/i18n/zh.json index e16bc0a..b6114f4 100644 --- a/internal/web/static/i18n/zh.json +++ b/internal/web/static/i18n/zh.json @@ -515,5 +515,39 @@ "src.sampled_if": "采样接口:{v}", "src.dir_yes": "带方向字段(61)", "src.dir_no": "不带方向字段(61),采样接口按流量推断", -"if.capture_only": "接口对账用来对比 sFlow、NetFlow、IPFIX 设备的流量和接口计数器。本机网卡抓包没有设备接口和计数器,所以这里没有可对比的内容。" +"if.capture_only": "接口对账用来对比 sFlow、NetFlow、IPFIX 设备的流量和接口计数器。本机网卡抓包没有设备接口和计数器,所以这里没有可对比的内容。", +"nav.account": "账户", +"acct.title_pw": "修改密码", +"acct.old": "当前密码", +"acct.new": "新密码", +"acct.confirm": "再输一次新密码", +"acct.save": "保存", +"acct.saved": "已保存", +"acct.must": "这是新安装的默认密码。请先设置一个新密码,再开始使用。", +"acct.rule": "至少 8 个字符,不能是 traffic66。", +"acct.err_wrong_old": "当前密码不对。", +"acct.err_too_short": "新密码至少要 8 个字符。", +"acct.err_default": "新密码不能是默认密码 traffic66。", +"acct.err_mismatch": "两次输入的新密码不一样。", +"acct.err_bad_user": "用户名不能为空,也不能含空格或冒号。", +"acct.err_self": "不能删除自己。", +"acct.err_fixed": "本次运行的密码来自启动参数,不能在这里修改。", +"acct.err_not_admin": "只有管理员能管理用户。", +"acct.fixed": "本次运行使用启动参数(-password 或 TRAFFIC66_PASSWORD)里的密码,不能在页面上修改。", +"acct.users": "用户管理", +"acct.users_sub": "所有用户能看和能改的内容相同,只有管理员能管理用户", +"acct.user": "用户名", +"acct.add": "添加用户", +"acct.reset": "重置密码", +"acct.delete": "删除", +"acct.admin_tag": "管理员", +"acct.confirm_del": "删除用户 {u}?已登录的浏览器会退出。", +"acct.added": "已保存", +"acct.ldap": "LDAP / AD 登录", +"acct.dev": "开发中", +"acct.ldap_dev": "开发中:以后可以用公司的 LDAP 或 Active Directory 账号登录 traffic66。", +"acct.ldap_server": "服务器地址", +"acct.ldap_base": "Base DN", +"login.hint": "首次登录:用户 {u},密码 {p}", +"login.hint_demo": "演示:用户 {u},密码 {p}" } diff --git a/internal/web/static/index.html b/internal/web/static/index.html index f41415b..784e4f9 100644 --- a/internal/web/static/index.html +++ b/internal/web/static/index.html @@ -34,6 +34,7 @@
+
@@ -62,6 +63,7 @@

+
@@ -76,6 +78,7 @@

+