Check-in [4ef059bc2a]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Omit the "ssl-acme" setting. Access to ".well-known" is now controlled by the --acme command-line option on "fossil http" and "fossil server". This change is required for when those commands specify a directory rather than a particular repository, since without a specific repository, there are no settings to check.
Timelines: family | ancestors | descendants | both | ssl-server
Files: files | file ages | folders
SHA3-256: 4ef059bc2a73205e19b2f3eeb04f7b4de0f0164c1d2f2bb57227c74fc202f12b
User & Date: drh 2021-12-28 19:00:38.745
Context
2021-12-28
19:04
Add initial support for SSL (TLS) servers on unix using "fossil server" or "fossil http". Rename the "tls-config" command to "ssl-config". Extend that command to support specifying certificates. Add support for delivering content from the ".well-known" directory to support obtaining certs from Let's Encrypt. check-in: f6263bb641 user: drh tags: trunk
19:00
Omit the "ssl-acme" setting. Access to ".well-known" is now controlled by the --acme command-line option on "fossil http" and "fossil server". This change is required for when those commands specify a directory rather than a particular repository, since without a specific repository, there are no settings to check. Closed-Leaf check-in: 4ef059bc2a user: drh tags: ssl-server
18:17
Fix the --files option on "fossil http" so that if a glob pattern does not begin with '*' then it will match beginning with the "/" of the PATH_INFO. check-in: 5ac65aa496 user: drh tags: ssl-server
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/db.c.
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
** users can not be deleted.
*/
/*
** SETTING: ssh-command      width=40 sensitive
** The command used to talk to a remote machine with  the "ssh://" protocol.
*/

/*
** SETTING: ssl-acme         boolean default=off
** If true, allow web pages with a path of "/.well-known/..." to retrieve
** files stored in the ".well-known" subdirectory in the same directory as
** the repository.  This is needed by tools such as "certbot" to verify a
** certificate signing request.
*/
/*
** SETTING: ssl-ca-location  width=40 sensitive
** The full pathname to a file containing PEM encoded
** CA root certificates, or a directory of certificates
** with filenames formed from the certificate hashes as
** required by OpenSSL.
**







<
<
<
<
<
<
<







4262
4263
4264
4265
4266
4267
4268







4269
4270
4271
4272
4273
4274
4275
** users can not be deleted.
*/
/*
** SETTING: ssh-command      width=40 sensitive
** The command used to talk to a remote machine with  the "ssh://" protocol.
*/








/*
** SETTING: ssl-ca-location  width=40 sensitive
** The full pathname to a file containing PEM encoded
** CA root certificates, or a directory of certificates
** with filenames formed from the certificate hashes as
** required by OpenSSL.
**
Changes to src/http_ssl.c.
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
**
** This command is used to view or modify the TLS (Transport Layer
** Security) configuration for Fossil.  TLS (formerly SSL) is the
** encryption technology used for secure HTTPS transport.
**
** Sub-commands:
**
**   acme ON/OFF                 Activate or deactivate web access to files in
**                               the "./well-known" directory.  This must be on
**                               to support "certbot".
**
**   clear-cert                  Remove information about server certificates.
**                               This is a subset of the "scrub" command.
**
**   load-cert PEM-FILES...      Identify server certificate files. These
**                               should be in the PEM format.  There are
**                               normally two files, the certificate and the
**                               private-key.  By default, the text of both







<
<
<
<







854
855
856
857
858
859
860




861
862
863
864
865
866
867
**
** This command is used to view or modify the TLS (Transport Layer
** Security) configuration for Fossil.  TLS (formerly SSL) is the
** encryption technology used for secure HTTPS transport.
**
** Sub-commands:
**




**   clear-cert                  Remove information about server certificates.
**                               This is a subset of the "scrub" command.
**
**   load-cert PEM-FILES...      Identify server certificate files. These
**                               should be in the PEM format.  There are
**                               normally two files, the certificate and the
**                               private-key.  By default, the text of both
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
  if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){
    zCmd = "show";
    nCmd = 4;
  }else{
    zCmd = g.argv[2];
    nCmd = strlen(zCmd);
  }
  if( strncmp("acme",zCmd,nCmd)==0 ){
    if( g.argc!=4 ) usage("acme ON/OFF");
    db_unprotect(PROTECT_CONFIG);
    if( is_truth(g.argv[3]) ){
      db_set_int("ssl-acme",1,0);
    }else if( is_false(g.argv[3]) ){
      db_unset("ssl-acme",0);
    }else{
      fossil_fatal("unknown argument: \"%s\"", g.argv[3]);
    }
    db_protect_pop();
  }else
  if( strncmp("clear-cert",zCmd,nCmd)==0 && nCmd>=4 ){
    int bForce = find_option("force","f",0)!=0;
    verify_all_options();
    if( !bForce ){
      Blob ans;
      char cReply;
      prompt_user(







<
<
<
<
<
<
<
<
<
<
<
<







889
890
891
892
893
894
895












896
897
898
899
900
901
902
  if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){
    zCmd = "show";
    nCmd = 4;
  }else{
    zCmd = g.argv[2];
    nCmd = strlen(zCmd);
  }












  if( strncmp("clear-cert",zCmd,nCmd)==0 && nCmd>=4 ){
    int bForce = find_option("force","f",0)!=0;
    verify_all_options();
    if( !bForce ){
      Blob ans;
      char cReply;
      prompt_user(
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
         "  This setting is the name of a file that contains the PEM-format\n"
         "  certificate and private-key used by Fossil clients to authentice\n"
         "  with servers. Few servers actually require this, so this setting\n"
         "  is usually blank.\n\n"
      );
    }

    fossil_print("ssl-acme:          %s\n",
           db_get_boolean("ssl-acme",0) ? "on" : "off");
    if( verbose ){
      fossil_print("\n"
         "  This setting enables web access to files in the \".well-known\"\n"
         "  subdirectory in the same directory as the repository. Such access\n"
         "  is required to obtain a certificate from services like\n"
         "  \"Let's Encrypt\" using the tools like \"certbot\".\n\n"
      );
    }

    zValue = db_get("ssl-cert",0);
    if( zValue ){
      fossil_print("ssl-cert:          (%d-byte PEM)\n", (int)strlen(zValue));
    }else{
      fossil_print("ssl-cert:\n");
    }
    if( verbose ){







<
<
<
<
<
<
<
<
<
<
<







1079
1080
1081
1082
1083
1084
1085











1086
1087
1088
1089
1090
1091
1092
         "  This setting is the name of a file that contains the PEM-format\n"
         "  certificate and private-key used by Fossil clients to authentice\n"
         "  with servers. Few servers actually require this, so this setting\n"
         "  is usually blank.\n\n"
      );
    }












    zValue = db_get("ssl-cert",0);
    if( zValue ){
      fossil_print("ssl-cert:          (%d-byte PEM)\n", (int)strlen(zValue));
    }else{
      fossil_print("ssl-cert:\n");
    }
    if( verbose ){
1202
1203
1204
1205
1206
1207
1208

1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
       zCmd);
  }
}

/*
** WEBPAGE: .well-known
**

** If the "ssl-acme" setting is true, then this page returns the content
** of files found in the ".well-known" subdirectory of the same directory
** that contains the repository file.  This facilitates Automated Certificate
** Management using tools like "certbot".
**
** The content is returned directly, without any interpretation, using
** a generic mimetype.
*/
void wellknown_page(void){
  char *zPath = 0;
  const char *zTail = P("name");
  Blob content;
  int i;
  char c;
  if( !db_get_boolean("ssl-acme",0) ) goto wellknown_notfound;
  if( g.zRepositoryName==0 ) goto wellknown_notfound;
  if( zTail==0 ) goto wellknown_notfound;
  zPath = mprintf("%z/.well-known/%s", file_dirname(g.zRepositoryName), zTail);
  for(i=0; (c = zTail[i])!=0; i++){
    if( fossil_isalnum(c) ) continue;
    if( c=='.' ){
      if( i==0 || zTail[i-1]=='/' || zTail[i-1]=='.' ) goto wellknown_notfound;







>
|
|
|











|







1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
       zCmd);
  }
}

/*
** WEBPAGE: .well-known
**
** If the "--acme" option was supplied to "fossil server" or "fossil http" or
** similar, then this page returns the content of files found in the
** ".well-known" subdirectory of the same directory that contains the
** repository file.  This facilitates Automated Certificate
** Management using tools like "certbot".
**
** The content is returned directly, without any interpretation, using
** a generic mimetype.
*/
void wellknown_page(void){
  char *zPath = 0;
  const char *zTail = P("name");
  Blob content;
  int i;
  char c;
  if( !g.fAllowACME ) goto wellknown_notfound;
  if( g.zRepositoryName==0 ) goto wellknown_notfound;
  if( zTail==0 ) goto wellknown_notfound;
  zPath = mprintf("%z/.well-known/%s", file_dirname(g.zRepositoryName), zTail);
  for(i=0; (c = zTail[i])!=0; i++){
    if( fossil_isalnum(c) ) continue;
    if( c=='.' ){
      if( i==0 || zTail[i-1]=='/' || zTail[i-1]=='.' ) goto wellknown_notfound;
Changes to src/main.c.
164
165
166
167
168
169
170

171
172
173
174
175
176
177
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  int fSqlPrint;          /* True if --sqlprint flag is present */
  int fCgiTrace;          /* True if --cgitrace is enabled */
  int fQuiet;             /* True if -quiet flag is present */
  int fJail;              /* True if running with a chroot jail */
  int fHttpTrace;         /* Trace outbound HTTP requests */
  int fAnyTrace;          /* Any kind of tracing */

  char *zHttpAuth;        /* HTTP Authorization user:pass information */
  int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
  int fSshTrace;          /* Trace the SSH setup traffic */
  int fSshClient;         /* HTTP client flags for SSH client */
  int fNoHttpCompress;    /* Do not compress HTTP traffic (for debugging) */
  char *zSshCmd;          /* SSH command string */
  const char *zHttpCmd;   /* External program to do HTTP requests */







>







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  int fSqlPrint;          /* True if --sqlprint flag is present */
  int fCgiTrace;          /* True if --cgitrace is enabled */
  int fQuiet;             /* True if -quiet flag is present */
  int fJail;              /* True if running with a chroot jail */
  int fHttpTrace;         /* Trace outbound HTTP requests */
  int fAnyTrace;          /* Any kind of tracing */
  int fAllowACME;         /* Deliver files from .well-known */
  char *zHttpAuth;        /* HTTP Authorization user:pass information */
  int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
  int fSshTrace;          /* Trace the SSH setup traffic */
  int fSshClient;         /* HTTP client flags for SSH client */
  int fNoHttpCompress;    /* Do not compress HTTP traffic (for debugging) */
  char *zSshCmd;          /* SSH command string */
  const char *zHttpCmd;   /* External program to do HTTP requests */
1698
1699
1700
1701
1702
1703
1704

1705

1706
1707
1708
1709
1710
1711
1712
#endif
        if( c=='/' ) continue;
        if( c=='_' ) continue;
        if( c=='-' && zRepo[j-1]!='/' ) continue;
        if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
          continue;
        }

        if( c=='.' && strncmp(&zRepo[j-1],"/.well-known/",12)==0 && j==nBase+1){

          /* We allow .well-known as the top-level directory for ACME */
          continue;
        }
        /* If we reach this point, it means that the request URI contains
        ** an illegal character or character combination.  Provoke a
        ** "Not Found" error. */
        szFile = 1;







>
|
>







1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
#endif
        if( c=='/' ) continue;
        if( c=='_' ) continue;
        if( c=='-' && zRepo[j-1]!='/' ) continue;
        if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
          continue;
        }
        if( c=='.' && g.fAllowACME && j==nBase+1
         && strncmp(&zRepo[j-1],"/.well-known/",12)==0
        ){
          /* We allow .well-known as the top-level directory for ACME */
          continue;
        }
        /* If we reach this point, it means that the request URI contains
        ** an illegal character or character combination.  Provoke a
        ** "Not Found" error. */
        szFile = 1;
1772
1773
1774
1775
1776
1777
1778















1779
1780
1781
1782
1783
1784
1785
          Blob content;
          blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
          cgi_set_content_type(zMimetype);
          cgi_set_content(&content);
          cgi_reply();
          return;
        }















        zRepo[j] = '.';
      }

      /* If we reach this point, it means that the search of the PATH_INFO
      ** string is finished.  Either zRepo contains the name of the
      ** repository to be used, or else no repository could be found and
      ** some kind of error response is required.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
          Blob content;
          blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
          cgi_set_content_type(zMimetype);
          cgi_set_content(&content);
          cgi_reply();
          return;
        }

        /* In support of the ACME protocol, files under the .well-known/
        ** directory is always accepted.
        */
        if( g.fAllowACME
         && strncmp(&zRepo[nBase],"/.well-known/",12)==0
         && file_isfile(zCleanRepo, ExtFILE)
        ){
          Blob content;
          blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
          cgi_set_content_type(mimetype_from_name(zRepo));
          cgi_set_content(&content);
          cgi_reply();
          return;
        }
        zRepo[j] = '.';
      }

      /* If we reach this point, it means that the search of the PATH_INFO
      ** string is finished.  Either zRepo contains the name of the
      ** repository to be used, or else no repository could be found and
      ** some kind of error response is required.
2612
2613
2614
2615
2616
2617
2618

2619
2620
2621
2622
2623
2624
2625
** thus also no redirecting from http: to https: will take place.
**
** If the --localauth option is given, then automatic login is performed
** for requests coming from localhost, if the "localauth" setting is not
** enabled.
**
** Options:

**   --baseurl URL       base URL (useful with reverse proxies)
**   --chroot DIR        Use directory for chroot instead of repository path.
**   --ckout-alias N     Treat URIs of the form /doc/N/... as if they were
**                          /doc/ckout/...
**   --extroot DIR       document root for the /ext extension mechanism
**   --files GLOB        comma-separate glob patterns for static file to serve
**   --host NAME         specify hostname of the server







>







2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
** thus also no redirecting from http: to https: will take place.
**
** If the --localauth option is given, then automatic login is performed
** for requests coming from localhost, if the "localauth" setting is not
** enabled.
**
** Options:
**   --acme              Deliver files from the ".well-known" subdirectory
**   --baseurl URL       base URL (useful with reverse proxies)
**   --chroot DIR        Use directory for chroot instead of repository path.
**   --ckout-alias N     Treat URIs of the form /doc/N/... as if they were
**                          /doc/ckout/...
**   --extroot DIR       document root for the /ext extension mechanism
**   --files GLOB        comma-separate glob patterns for static file to serve
**   --host NAME         specify hostname of the server
2726
2727
2728
2729
2730
2731
2732

2733
2734
2735
2736
2737
2738
2739
  zHost = find_option("host", 0, 1);
  if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
  g.zMainMenuFile = find_option("mainmenu",0,1);
  if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
    fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
  }
  decode_ssl_options();


  /* We should be done with options.. */
  verify_all_options();
  if( g.httpUseSSL ){
    if( useSCGI ){
      fossil_fatal("SSL not (yet) supported for SCGI");
    }







>







2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
  zHost = find_option("host", 0, 1);
  if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
  g.zMainMenuFile = find_option("mainmenu",0,1);
  if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
    fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
  }
  decode_ssl_options();
  if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;

  /* We should be done with options.. */
  verify_all_options();
  if( g.httpUseSSL ){
    if( useSCGI ){
      fossil_fatal("SSL not (yet) supported for SCGI");
    }
2942
2943
2944
2945
2946
2947
2948

2949
2950
2951
2952
2953
2954
2955
** having to log in.  This can be disabled by turning off the "localauth"
** setting.  Automatic login for the "server" command is available if the
** --localauth option is present and the "localauth" setting is off and the
** connection is from localhost.  The "ui" command also enables --repolist
** by default.
**
** Options:

**   --baseurl URL       Use URL as the base (useful for reverse proxies)
**   --chroot DIR        Use directory for chroot instead of repository path.
**   --ckout-alias NAME  Treat URIs of the form /doc/NAME/... as if they were
**                       /doc/ckout/...
**   --create            Create a new REPOSITORY if it does not already exist
**   --extroot DIR       Document root for the /ext extension mechanism
**   --files GLOBLIST    Comma-separated list of glob patterns for static files







>







2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
** having to log in.  This can be disabled by turning off the "localauth"
** setting.  Automatic login for the "server" command is available if the
** --localauth option is present and the "localauth" setting is off and the
** connection is from localhost.  The "ui" command also enables --repolist
** by default.
**
** Options:
**   --acme              Deliver files from the ".well-known" subdirectory.
**   --baseurl URL       Use URL as the base (useful for reverse proxies)
**   --chroot DIR        Use directory for chroot instead of repository path.
**   --ckout-alias NAME  Treat URIs of the form /doc/NAME/... as if they were
**                       /doc/ckout/...
**   --create            Create a new REPOSITORY if it does not already exist
**   --extroot DIR       Document root for the /ext extension mechanism
**   --files GLOBLIST    Comma-separated list of glob patterns for static files
3075
3076
3077
3078
3079
3080
3081

3082
3083
3084
3085
3086
3087
3088
    flags |= HTTP_SERVER_LOCALHOST;
  }
  g.zCkoutAlias = find_option("ckout-alias",0,1);
  g.zMainMenuFile = find_option("mainmenu",0,1);
  if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
    fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
  }


  /* Undocumented option:  --debug-nofork
  **
  ** This sets the HTTP_SERVER_NOFORK flag, which causes only the
  ** very first incoming TCP/IP connection to be processed.  Used for
  ** debugging, since debugging across a fork() can be tricky
  */







>







3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
    flags |= HTTP_SERVER_LOCALHOST;
  }
  g.zCkoutAlias = find_option("ckout-alias",0,1);
  g.zMainMenuFile = find_option("mainmenu",0,1);
  if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
    fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
  }
  if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;

  /* Undocumented option:  --debug-nofork
  **
  ** This sets the HTTP_SERVER_NOFORK flag, which causes only the
  ** very first incoming TCP/IP connection to be processed.  Used for
  ** debugging, since debugging across a fork() can be tricky
  */