Parental Control for Linux Mint 22
I want my kids to learn how to use a computer but at the same time I do not want them to be exposed to everything the internet has to offer.
There are two mayor aspects I want to address:
- Usage duration
- Internet access
Usage duration (Timekpr-nExT)
The usage duration in my case is very simple. I just want them to have a specified time each day with an exception für weekends. To archive that, I installed Timekpr-nExT which can be found in the Linux Mint Saftware Store.
- Open the
(SU) Timekpr-nExT Control Panel (superuser mode)menu entry - Enter your password to get root access
- Select the
Usernameof your child's account - Under
Linit configurationset theWeek day limitsfor each day andHour intervalsfor each day while selected(!) and then hitverifyfor each entry(!) - I did not use the PlayTime feature
- Click
Apply daily limitswhen done

This set the usage time allowance and makes sure the child user is logged out when the time expired.
Simple remote page
Install a web server like nginx or lighttpd with php-fpm configured. Then create these files in the wwwroot directory configured for the server. Alternatively, hijack CTParental in /var/www/CTParental and just create these files in a sub-directory.
data.php:
<?php
$adminPass = ''; // <- set hash here
$account = 'child'; // <- set child's user account here
$do = $_GET['do'];
switch ($do) {
case 'info':
$result = shell_exec('sudo /usr/bin/timekpra --userinfo ' . $account);
$options = explode("\n", $result);
$data = array();
foreach ($options as $line) {
if ($line == '') continue;
if (str_starts_with($line, '#')) continue;
$parts = explode(': ', $line, 2);
if (count($parts) == 2) {
$data[$parts[0]] = $parts[1];
}
}
header('Content-Type: application/json; charset=utf-8');
print(json_encode($data));
break;
case 'pass':
print(password_hash($_GET['pass'], PASSWORD_DEFAULT));
break;
case 'settimeleft':
$pass = $_GET['pass'] or die('No pass!');
if (!password_verify($pass, $adminPass))
die('Wrong pass!');
$time = $_GET['time'] or die('No time!');
if (preg_match('/^([0-9]{1,8})$/', $time, $matches) == 0)
die('Invalid time!');
$result = shell_exec('sudo /usr/bin/timekpra --settimeleft ' . $account . ' = ' . $matches[0]);
$data = array();
$data['result'] = $result;
header('Content-Type: application/json; charset=utf-8');
print(json_encode($data));
break;
default:
phpinfo();
break;
}
?>
index.htm:
<!DOCTYPE html>
<html>
<head>
<title>TimeKpr</title>
<meta charset="utf-8" />
<script type="text/javascript">
function toHumanReadable(secs) {
if (typeof(secs) != 'number') secs = parseInt(secs);
var result = '';
if (secs > 3600) {
result += Math.floor(secs / 3600).toString().padStart(2, '0') + ':';
secs = secs % 3600;
}
result += Math.floor(secs / 60).toString().padStart(2, '0') + ':';
secs = secs % 60;
result += Math.floor(secs).toString().padStart(2, '0');
return result;
}
function fromHumanReadable(time) {
var result = 0;
var parts = time.split(':');
for (var i = 0; i < parts.length; i++) {
var nr = parseInt(parts[i]);
result = result * 60 + nr;
}
return result;
}
function onUpdate(json) {
var timeLeft = document.getElementById('timeLeft');
if (json['TIME_LEFT_DAY']) {
document.getElementById('timeLeft').value = toHumanReadable(json['TIME_LEFT_DAY']);
}
if (json['TIME_SPENT_DAY']) {
document.getElementById('timeSpentDay').innerText = toHumanReadable(json['TIME_SPENT_DAY']);
}
if (json['TIME_SPENT_WEEK']) {
document.getElementById('timeSpentWeek').innerText = toHumanReadable(json['TIME_SPENT_WEEK']);
}
}
function setTimeLeft() {
var time = fromHumanReadable(document.getElementById('timeLeft').value);
var pass = encodeURIComponent(document.getElementById('pass').value);
fetch('data.php?do=settimeleft&pass='+pass+'&time='+time)
.then(response => response.json())
.then(data => {
console.log(data);
//window.location.reload();
})
.catch(error => console.error('Error:', error));
}
function init() {
fetch('data.php?do=info')
.then(response => response.json())
.then(data => { console.log(data); onUpdate(data); })
.catch(error => console.error('Error:', error));
}
</script>
</head>
<body onload="init();">
<h1>TimeKpr</h1>
<table>
<tr>
<td><b>Password:</b></td>
<td><input type="password" id="pass" /></td>
</tr>
<tr>
<td><b>Time spent week:</b></td>
<td><span id="timeSpentWeek"></span></td>
</tr>
<tr>
<td><b>Time spent today:</b></td>
<td><span id="timeSpentDay"></span></td>
</tr>
<tr>
<td><b>Time left:</b></td>
<td>
<input id="timeLeft" />
<button onclick="setTimeLeft();">Set</button>
</td>
</tr>
</table>
</body>
</html>
You'll have to allow the user www-data to exeute timekpra without a password - this is a bit over-reaching but I couldn't find a better way yet:
www-data ALL=NOPASSWD: ALL
Afterwards, you can create a password hash by opening a browser in private mode and opening the address:
http://localhost/data.php?do=pass&pass=YOUR_PASSWORD
Adjust your password and if you created this in a sub-directory, the path.
Internet access (CTParental)
Download the latest DEB version (Debian 13 in my case) from the GitLab CTParentalGroup releases: CTParental Releases
Then install it:
sudo apt install /path/to/downloaded.deb
After installation and setting up you can open the administration page admin.ct.local.
This is my whitelist for these porposes:
- MineCraft Microsoft login
- uBlock Filter lists
- FragFINN and some linked pages
Keep in mind that this is not a good list! GitHub for example is much too broad but I don't expect my child to access them.
.microsoft.com
.minecraft.com
.minecraft.net
.minecraftservices.com
.minecraft-services.net
.outlook.com
.outlook.de
.live.com
.xboxlive.com
.msftauth.net
.azureedge.net
.azure.com
.assets.adobedtm.com
.office365.com
.github.com
.githubassets.com
.githubusercontent.com
.github.io
.gitlab.com
.easylist.to
.pgl.yoyo.org
.fanboy.co.nz
.adguard.com
.adguard-dns.io
.adblockplus.org
.fragfinn.de
.westermann.de
.local
.zum.de
.telegram.org
.t.me
.telesco.pe
.telegram.me
.tg.dev
.kika.de
.logo.de
.internet-abc.de
.radiofuechse.de
.digitallearninglab.de
.kleinezeitung.at
.homeschooling4kids.at
.tierchenwelt.de
.kindernetz.de
.pbskids.org
.bfn.de
.schubu.org
.matheretter.de
.wdrmaus.de
.kindersache.de
.rofu.de
.sivakids.de
.kinderzeitmaschine.de
.fessie.de
.labbe.de
.singkinderlieder.de
.baumhausbande.com
.liederprojekt.org
.kruschel.de
.milchland.de
.buttinette.com
.naturpark-detektive.de
.natur-vision.de
.ideenlabor-natur.de
.natur-ranger.de
.natur-beobachtungen.de
.natur-entdecken-online.de
.naturinfo.ch
.das-macht-schule.net
.knax.de
.learnattack.de
.meteoros.de
.lehrer-online.de
.mail4kidz.de
.malvorlagen-bilder.de
.kinderweltreise.de
.kinderuni.online
.kinderfotopreis.de
.kaenguru-online.de
.berufe-lexikon.de
.kakadu.de
.wissensschule.de
.wikimedia.org
.code-it-studio.de
.handysektor.de
.readspeaker.com
.ebhelm.de